diff --git a/lib/getid3/extension.cache.dbm.php b/lib/getid3/extension.cache.dbm.php index a9332b9283d6ae1d0305024517c3a9f1ffaab1c2..2d72d9553103e30f3aa769d72d88b4e1abbc3930 100644 --- a/lib/getid3/extension.cache.dbm.php +++ b/lib/getid3/extension.cache.dbm.php @@ -1,10 +1,10 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // // // // extension.cache.dbm.php - part of getID3() // // Please see readme.txt for more information // @@ -72,8 +72,36 @@ class getID3_cached_dbm extends getID3 { - - // public: constructor - see top of this file for cache type and cache_options + /** + * @var resource + */ + private $dba; + + /** + * @var resource|bool + */ + private $lock; + + /** + * @var string + */ + private $cache_type; + + /** + * @var string + */ + private $dbm_filename; + + /** + * constructor - see top of this file for cache type and cache_options + * + * @param string $cache_type + * @param string $dbm_filename + * @param string $lock_filename + * + * @throws Exception + * @throws getid3_exception + */ public function __construct($cache_type, $dbm_filename, $lock_filename) { // Check for dba extension @@ -141,7 +169,9 @@ class getID3_cached_dbm extends getID3 - // public: destructor + /** + * destructor + */ public function __destruct() { // Close dbm file @@ -156,7 +186,11 @@ class getID3_cached_dbm extends getID3 - // public: clear cache + /** + * clear cache + * + * @throws Exception + */ public function clear_cache() { // Close dbm file @@ -178,8 +212,16 @@ class getID3_cached_dbm extends getID3 - // public: analyze file - public function analyze($filename) { + /** + * clear cache + * + * @param string $filename + * @param int $filesize + * @param string $original_filename + * + * @return mixed + */ + public function analyze($filename, $filesize=null, $original_filename='') { if (file_exists($filename)) { @@ -199,7 +241,7 @@ class getID3_cached_dbm extends getID3 $result = parent::analyze($filename); // Save result - if (file_exists($filename)) { + if (isset($key) && file_exists($filename)) { dba_insert($key, serialize($result), $this->dba); } diff --git a/lib/getid3/extension.cache.mysql.php b/lib/getid3/extension.cache.mysql.php index b572676a0bb275e0bfe74d85086ba93a80248a37..2dd2b12a72118cdab3637d4759f405f259b54daf 100644 --- a/lib/getid3/extension.cache.mysql.php +++ b/lib/getid3/extension.cache.mysql.php @@ -1,10 +1,10 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // // // // extension.cache.mysql.php - part of getID3() // // Please see readme.txt for more information // @@ -73,13 +73,34 @@ class getID3_cached_mysql extends getID3 { - - // private vars + /** + * @var resource + */ private $cursor; - private $connection; + /** + * @var resource + */ + private $connection; - // public: constructor - see top of this file for cache type and cache_options + /** + * @var string + */ + private $table; + + + /** + * constructor - see top of this file for cache type and cache_options + * + * @param string $host + * @param string $database + * @param string $username + * @param string $password + * @param string $table + * + * @throws Exception + * @throws getid3_exception + */ public function __construct($host, $database, $username, $password, $table='getid3_cache') { // Check for mysql support @@ -124,7 +145,9 @@ class getID3_cached_mysql extends getID3 - // public: clear cache + /** + * clear cache + */ public function clear_cache() { $this->cursor = mysql_query('DELETE FROM `'.mysql_real_escape_string($this->table).'`', $this->connection); @@ -133,9 +156,18 @@ class getID3_cached_mysql extends getID3 - // public: analyze file + /** + * analyze file + * + * @param string $filename + * @param int $filesize + * @param string $original_filename + * + * @return mixed + */ public function analyze($filename, $filesize=null, $original_filename='') { + $filetime = 0; if (file_exists($filename)) { // Short-hands @@ -174,7 +206,11 @@ class getID3_cached_mysql extends getID3 - // private: (re)create sql table + /** + * (re)create sql table + * + * @param bool $drop + */ private function create_table($drop=false) { $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` ('; @@ -183,7 +219,7 @@ class getID3_cached_mysql extends getID3 $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `value` LONGTEXT NOT NULL'; - $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci'; + $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`))'; $this->cursor = mysql_query($SQLquery, $this->connection); echo mysql_error($this->connection); } diff --git a/lib/getid3/extension.cache.mysqli.php b/lib/getid3/extension.cache.mysqli.php index 3299caae005f4be93f7bcd6ee27bc4a069b52f01..44ea694ee566a8f171e5d7a8fcd131cc6cd953ed 100644 --- a/lib/getid3/extension.cache.mysqli.php +++ b/lib/getid3/extension.cache.mysqli.php @@ -1,14 +1,14 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // // // -// extension.cache.mysqli.php - part of getID3() // +// extension.cache.mysqli.php - part of getID3() // // Please see readme.txt for more information // -// /// +// // ///////////////////////////////////////////////////////////////// // // // This extension written by Allan Hansen <ahØartemis*dk> // @@ -72,12 +72,34 @@ class getID3_cached_mysqli extends getID3 { - // private vars + /** + * @var mysqli + */ private $mysqli; - private $cursor; + /** + * @var mysqli_result + */ + private $cursor; - // public: constructor - see top of this file for cache type and cache_options + /** + * @var string + */ + private $table; + + + /** + * constructor - see top of this file for cache type and cache_options + * + * @param string $host + * @param string $database + * @param string $username + * @param string $password + * @param string $table + * + * @throws Exception + * @throws getid3_exception + */ public function __construct($host, $database, $username, $password, $table='getid3_cache') { // Check for mysqli support @@ -121,16 +143,27 @@ class getID3_cached_mysqli extends getID3 } - // public: clear cache + /** + * clear cache + */ public function clear_cache() { $this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`'); $this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')'); } - // public: analyze file + /** + * analyze file + * + * @param string $filename + * @param int $filesize + * @param string $original_filename + * + * @return mixed + */ public function analyze($filename, $filesize=null, $original_filename='') { + $filetime = 0; if (file_exists($filename)) { // Short-hands @@ -168,7 +201,11 @@ class getID3_cached_mysqli extends getID3 } - // private: (re)create mysqli table + /** + * (re)create mysqli table + * + * @param bool $drop + */ private function create_table($drop=false) { $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` ('; $SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\''; @@ -176,8 +213,8 @@ class getID3_cached_mysqli extends getID3 $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\''; $SQLquery .= ', `value` LONGTEXT NOT NULL'; - $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci'; + $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`))'; $this->cursor = $this->mysqli->query($SQLquery); echo $this->mysqli->error; } -} \ No newline at end of file +} diff --git a/lib/getid3/extension.cache.sqlite3.php b/lib/getid3/extension.cache.sqlite3.php index 58d4dc884df46a830ef312ab246954716be86b0e..9e4a843692fbb8ddbef2026f5faeda689045a71d 100644 --- a/lib/getid3/extension.cache.sqlite3.php +++ b/lib/getid3/extension.cache.sqlite3.php @@ -1,21 +1,27 @@ <?php -///////////////////////////////////////////////////////////////////////////////// -/// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////////////////////// -/// // -// extension.cache.sqlite3.php - part of getID3() // -// Please see readme.txt for more information // -// /// -///////////////////////////////////////////////////////////////////////////////// -/// // -// MySQL extension written by Allan Hansen <ahØartemis*dk> // -// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> // -// MySQL extension was reworked for SQLite3 by Karl G. Holz <newaeonØmac*com> // -// /// -///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich <info@getid3.org> // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// // +// extension.cache.mysqli.php - part of getID3() // +// Please see readme.txt for more information // +// // +///////////////////////////////////////////////////////////////// +// // +// extension.cache.sqlite3.php - part of getID3() // +// Please see readme.txt for more information // +// // +///////////////////////////////////////////////////////////////// +/// // +// MySQL extension written by Allan Hansen <ahØartemis*dk> // +// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> // +// MySQL extension was reworked for SQLite3 by // +// Karl G. Holz <newaeonØmac*com> // +// /// +///////////////////////////////////////////////////////////////// + /** * This is a caching extension for getID3(). It works the exact same * way as the getID3 class, but return cached information much faster @@ -89,14 +95,35 @@ * * */ -class getID3_cached_sqlite3 extends getID3 { +class getID3_cached_sqlite3 extends getID3 +{ + /** + * hold the sqlite db + * + * @var SQLite3 Resource + */ + private $db; /** - * __construct() - * @param string $table holds name of sqlite table - * @return type - */ + * table to use for caching + * + * @var string $table + */ + private $table; + + /** + * @param string $table holds name of sqlite table + * @param boolean $hide + * + * @throws getid3_exception + * @throws Exception + */ public function __construct($table='getid3_cache', $hide=false) { + // Check for SQLite3 support + if (!function_exists('sqlite_open')) { + throw new Exception('PHP not compiled with SQLite3 support.'); + } + $this->table = $table; // Set table $file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite'; if ($hide) { @@ -106,7 +133,7 @@ class getID3_cached_sqlite3 extends getID3 { $db = $this->db; $this->create_table(); // Create cache table if not exists $version = ''; - $sql = $this->version_check; + $sql = $this->getQuery('version_check'); $stmt = $db->prepare($sql); $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT); $result = $stmt->execute(); @@ -114,39 +141,27 @@ class getID3_cached_sqlite3 extends getID3 { if ($version != getID3::VERSION) { // Check version number and clear cache if changed $this->clear_cache(); } - return parent::__construct(); + parent::__construct(); } /** - * close the database connection - */ + * close the database connection + */ public function __destruct() { $db=$this->db; $db->close(); } /** - * hold the sqlite db - * @var SQLite Resource - */ - private $db; - - /** - * table to use for caching - * @var string $table - */ - private $table; - - /** - * clear the cache - * @access private - * @return type - */ + * clear the cache + * + * @return SQLite3Result + */ private function clear_cache() { $db = $this->db; - $sql = $this->delete_cache; + $sql = $this->getQuery('delete_cache'); $db->exec($sql); - $sql = $this->set_version; + $sql = $this->getQuery('set_version'); $stmt = $db->prepare($sql); $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT); $stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT); @@ -155,10 +170,14 @@ class getID3_cached_sqlite3 extends getID3 { } /** - * analyze file and cache them, if cached pull from the db - * @param type $filename - * @return boolean - */ + * analyze file and cache them, if cached pull from the db + * + * @param string $filename + * @param integer $filesize + * @param string $original_filename + * + * @return mixed|false + */ public function analyze($filename, $filesize=null, $original_filename='') { if (!file_exists($filename)) { return false; @@ -171,7 +190,7 @@ class getID3_cached_sqlite3 extends getID3 { $dirname = dirname($filename); // Lookup file $db = $this->db; - $sql = $this->get_id3_data; + $sql = $this->getQuery('get_id3_data'); $stmt = $db->prepare($sql); $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); $stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER); @@ -184,7 +203,7 @@ class getID3_cached_sqlite3 extends getID3 { // if it hasn't been analyzed before, then do it now $analysis = parent::analyze($filename, $filesize, $original_filename); // Save result - $sql = $this->cache_file; + $sql = $this->getQuery('cache_file'); $stmt = $db->prepare($sql); $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); $stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT); @@ -197,30 +216,31 @@ class getID3_cached_sqlite3 extends getID3 { } /** - * create data base table - * this is almost the same as MySQL, with the exception of the dirname being added - * @return type - */ + * create data base table + * this is almost the same as MySQL, with the exception of the dirname being added + * + * @return bool + */ private function create_table() { $db = $this->db; - $sql = $this->make_table; + $sql = $this->getQuery('make_table'); return $db->exec($sql); } /** - * get cached directory - * - * This function is not in the MySQL extention, it's ment to speed up requesting multiple files - * which is ideal for podcasting, playlists, etc. - * - * @access public - * @param string $dir directory to search the cache database for - * @return array return an array of matching id3 data - */ + * get cached directory + * + * This function is not in the MySQL extention, it's ment to speed up requesting multiple files + * which is ideal for podcasting, playlists, etc. + * + * @param string $dir directory to search the cache database for + * + * @return array return an array of matching id3 data + */ public function get_cached_dir($dir) { $db = $this->db; $rows = array(); - $sql = $this->get_cached_dir; + $sql = $this->getQuery('get_cached_dir'); $stmt = $db->prepare($sql); $stmt->bindValue(':dirname', $dir, SQLITE3_TEXT); $res = $stmt->execute(); @@ -231,12 +251,15 @@ class getID3_cached_sqlite3 extends getID3 { } /** - * use the magical __get() for sql queries - * - * access as easy as $this->{case name}, returns NULL if query is not found - */ - public function __get($name) { - switch($name) { + * returns NULL if query is not found + * + * @param string $name + * + * @return null|string + */ + public function getQuery($name) + { + switch ($name) { case 'version_check': return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'"; break; @@ -253,14 +276,28 @@ class getID3_cached_sqlite3 extends getID3 { return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)"; break; case 'make_table': - //return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))"; return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))"; break; case 'get_cached_dir': return "SELECT val FROM $this->table WHERE dirname = :dirname"; break; + default: + return null; } - return null; + } + + /** + * use the magical __get() for sql queries + * + * access as easy as $this->{case name}, returns NULL if query is not found + * + * @param string $name + * + * @return string + * @deprecated use getQuery() instead + */ + public function __get($name) { + return $this->getQuery($name); } } diff --git a/lib/getid3/getid3.lib.php b/lib/getid3/getid3.lib.php index 808f5921685152ea0f65b9ea8ceec175b758c48e..5c5a5dfacac2811e50622bc8223faf68c7b811a7 100644 --- a/lib/getid3/getid3.lib.php +++ b/lib/getid3/getid3.lib.php @@ -1,20 +1,27 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // // // // getid3.lib.php - part of getID3() // -// See readme.txt for more details // +// see readme.txt for more details // // /// ///////////////////////////////////////////////////////////////// class getid3_lib { - + /** + * @param string $string + * @param bool $hex + * @param bool $spaces + * @param string $htmlencoding + * + * @return string + */ public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') { $returnstring = ''; for ($i = 0; $i < strlen($string); $i++) { @@ -36,9 +43,14 @@ class getid3_lib return $returnstring; } + /** + * Truncates a floating-point number at the decimal point. + * + * @param float $floatnumber + * + * @return float|int returns int (if possible, otherwise float) + */ public static function trunc($floatnumber) { - // truncates a floating-point number at the decimal point - // returns int (if possible, otherwise float) if ($floatnumber >= 1) { $truncatednumber = floor($floatnumber); } elseif ($floatnumber <= -1) { @@ -52,7 +64,12 @@ class getid3_lib return $truncatednumber; } - + /** + * @param int|null $variable + * @param int $increment + * + * @return bool + */ public static function safe_inc(&$variable, $increment=1) { if (isset($variable)) { $variable += $increment; @@ -62,6 +79,11 @@ class getid3_lib return true; } + /** + * @param int|float $floatnum + * + * @return int|float + */ public static function CastAsInt($floatnum) { // convert to float if not already $floatnum = (float) $floatnum; @@ -77,6 +99,11 @@ class getid3_lib return $floatnum; } + /** + * @param int $num + * + * @return bool + */ public static function intValueSupported($num) { // check if integers are 64-bit static $hasINT64 = null; @@ -93,21 +120,36 @@ class getid3_lib return false; } + /** + * @param string $fraction + * + * @return float + */ public static function DecimalizeFraction($fraction) { list($numerator, $denominator) = explode('/', $fraction); return $numerator / ($denominator ? $denominator : 1); } - + /** + * @param string $binarynumerator + * + * @return float + */ public static function DecimalBinary2Float($binarynumerator) { $numerator = self::Bin2Dec($binarynumerator); $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); return ($numerator / $denominator); } - + /** + * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + * + * @param string $binarypointnumber + * @param int $maxbits + * + * @return array + */ public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html if (strpos($binarypointnumber, '.') === false) { $binarypointnumber = '0.'.$binarypointnumber; } elseif ($binarypointnumber{0} == '.') { @@ -129,9 +171,14 @@ class getid3_lib return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); } - + /** + * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + * + * @param float $floatvalue + * + * @return string + */ public static function Float2BinaryDecimal($floatvalue) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html $maxbits = 128; // to how many bits of precision should the calculations be taken? $intpart = self::trunc($floatvalue); $floatpart = abs($floatvalue - $intpart); @@ -145,9 +192,17 @@ class getid3_lib return $binarypointnumber; } - + /** + * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html + * + * @param float $floatvalue + * @param int $bits + * + * @return string|false + */ public static function Float2String($floatvalue, $bits) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html + $exponentbits = 0; + $fractionbits = 0; switch ($bits) { case 32: $exponentbits = 8; @@ -176,22 +231,34 @@ class getid3_lib return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); } - + /** + * @param string $byteword + * + * @return float|false + */ public static function LittleEndian2Float($byteword) { return self::BigEndian2Float(strrev($byteword)); } - + /** + * ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic + * + * @link http://www.psc.edu/general/software/packages/ieee/ieee.html + * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html + * + * @param string $byteword + * + * @return float|false + */ public static function BigEndian2Float($byteword) { - // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic - // http://www.psc.edu/general/software/packages/ieee/ieee.html - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html - $bitword = self::BigEndian2Bin($byteword); if (!$bitword) { return 0; } $signbit = $bitword{0}; + $floatvalue = 0; + $exponentbits = 0; + $fractionbits = 0; switch (strlen($byteword) * 8) { case 32: @@ -259,7 +326,14 @@ class getid3_lib return (float) $floatvalue; } - + /** + * @param string $byteword + * @param bool $synchsafe + * @param bool $signed + * + * @return int|float|false + * @throws Exception + */ public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { $intvalue = 0; $bytewordlen = strlen($byteword); @@ -288,12 +362,30 @@ class getid3_lib return self::CastAsInt($intvalue); } - + /** + * @param string $byteword + * @param bool $signed + * + * @return int|float|false + */ public static function LittleEndian2Int($byteword, $signed=false) { return self::BigEndian2Int(strrev($byteword), false, $signed); } + /** + * @param string $byteword + * + * @return string + */ + public static function LittleEndian2Bin($byteword) { + return self::BigEndian2Bin(strrev($byteword)); + } + /** + * @param string $byteword + * + * @return string + */ public static function BigEndian2Bin($byteword) { $binvalue = ''; $bytewordlen = strlen($byteword); @@ -303,7 +395,15 @@ class getid3_lib return $binvalue; } - + /** + * @param int $number + * @param int $minbytes + * @param bool $synchsafe + * @param bool $signed + * + * @return string + * @throws Exception + */ public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { if ($number < 0) { throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers'); @@ -324,7 +424,11 @@ class getid3_lib return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT); } - + /** + * @param int $number + * + * @return string + */ public static function Dec2Bin($number) { while ($number >= 256) { $bytes[] = (($number / 256) - (floor($number / 256))) * 256; @@ -338,7 +442,12 @@ class getid3_lib return $binstring; } - + /** + * @param string $binstring + * @param bool $signed + * + * @return int|float + */ public static function Bin2Dec($binstring, $signed=false) { $signmult = 1; if ($signed) { @@ -354,7 +463,11 @@ class getid3_lib return self::CastAsInt($decvalue * $signmult); } - + /** + * @param string $binstring + * + * @return string + */ public static function Bin2String($binstring) { // return 'hi' for input of '0110100001101001' $string = ''; @@ -365,7 +478,13 @@ class getid3_lib return $string; } - + /** + * @param int $number + * @param int $minbytes + * @param bool $synchsafe + * + * @return string + */ public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { $intstring = ''; while ($number > 0) { @@ -380,7 +499,12 @@ class getid3_lib return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); } - + /** + * @param array $array1 + * @param array $array2 + * + * @return array|false + */ public static function array_merge_clobber($array1, $array2) { // written by kcØhireability*com // taken from http://www.php.net/manual/en/function.array-merge-recursive.php @@ -398,7 +522,12 @@ class getid3_lib return $newarray; } - + /** + * @param array $array1 + * @param array $array2 + * + * @return array|false + */ public static function array_merge_noclobber($array1, $array2) { if (!is_array($array1) || !is_array($array2)) { return false; @@ -414,6 +543,12 @@ class getid3_lib return $newarray; } + /** + * @param array $array1 + * @param array $array2 + * + * @return array|false|null + */ public static function flipped_array_merge_noclobber($array1, $array2) { if (!is_array($array1) || !is_array($array2)) { return false; @@ -428,7 +563,11 @@ class getid3_lib return array_flip($newarray); } - + /** + * @param array $theArray + * + * @return bool + */ public static function ksort_recursive(&$theArray) { ksort($theArray); foreach ($theArray as $key => $value) { @@ -439,6 +578,12 @@ class getid3_lib return true; } + /** + * @param string $filename + * @param int $numextensions + * + * @return string + */ public static function fileextension($filename, $numextensions=1) { if (strstr($filename, '.')) { $reversedfilename = strrev($filename); @@ -454,7 +599,11 @@ class getid3_lib return ''; } - + /** + * @param int $seconds + * + * @return string + */ public static function PlaytimeString($seconds) { $sign = (($seconds < 0) ? '-' : ''); $seconds = round(abs($seconds)); @@ -464,30 +613,53 @@ class getid3_lib return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT); } - + /** + * @param int $macdate + * + * @return int|float + */ public static function DateMac2Unix($macdate) { // Macintosh timestamp: seconds since 00:00h January 1, 1904 // UNIX timestamp: seconds since 00:00h January 1, 1970 return self::CastAsInt($macdate - 2082844800); } - + /** + * @param string $rawdata + * + * @return float + */ public static function FixedPoint8_8($rawdata) { return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); } - + /** + * @param string $rawdata + * + * @return float + */ public static function FixedPoint16_16($rawdata) { return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); } - + /** + * @param string $rawdata + * + * @return float + */ public static function FixedPoint2_30($rawdata) { $binarystring = self::BigEndian2Bin($rawdata); return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); } + /** + * @param string $ArrayPath + * @param string $Separator + * @param mixed $Value + * + * @return array + */ public static function CreateDeepArray($ArrayPath, $Separator, $Value) { // assigns $Value to a nested array path: // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt') @@ -504,6 +676,12 @@ class getid3_lib return $ReturnedArray; } + /** + * @param array $arraydata + * @param bool $returnkey + * + * @return int|false + */ public static function array_max($arraydata, $returnkey=false) { $maxvalue = false; $maxkey = false; @@ -518,6 +696,12 @@ class getid3_lib return ($returnkey ? $maxkey : $maxvalue); } + /** + * @param array $arraydata + * @param bool $returnkey + * + * @return int|false + */ public static function array_min($arraydata, $returnkey=false) { $minvalue = false; $minkey = false; @@ -532,6 +716,11 @@ class getid3_lib return ($returnkey ? $minkey : $minvalue); } + /** + * @param string $XMLstring + * + * @return array|false + */ public static function XML2array($XMLstring) { if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) { // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html @@ -545,22 +734,42 @@ class getid3_lib return false; } + /** + * @param SimpleXMLElement|array $XMLobject + * + * @return array + */ public static function SimpleXMLelement2array($XMLobject) { if (!is_object($XMLobject) && !is_array($XMLobject)) { return $XMLobject; } - $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject); + $XMLarray = $XMLobject instanceof SimpleXMLElement ? get_object_vars($XMLobject) : $XMLobject; foreach ($XMLarray as $key => $value) { $XMLarray[$key] = self::SimpleXMLelement2array($value); } return $XMLarray; } - - // Allan Hansen <ahØartemis*dk> - // self::md5_data() - returns md5sum for a file from startuing position to absolute end position + /** + * self::md5_data() - returns md5sum for a file from startuing position to absolute end position + * + * @author Allan Hansen <ahØartemis*dk> + * + * @param string $file + * @param int $offset + * @param int $end + * @param string $algorithm + * + * @return string|false + * @throws Exception + * @throws getid3_exception + */ public static function hash_data($file, $offset, $end, $algorithm) { static $tempdir = ''; + $windows_call = null; + $unix_call = null; + $hash_length = null; + $hash_function = null; if (!self::intValueSupported($end)) { return false; } @@ -645,6 +854,15 @@ class getid3_lib return $result; } + /** + * @param string $filename_source + * @param string $filename_dest + * @param int $offset + * @param int $length + * + * @return bool + * @throws Exception + */ public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) { if (!self::intValueSupported($offset + $length)) { throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); @@ -657,21 +875,25 @@ class getid3_lib $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); $byteslefttowrite -= $byteswritten; } + fclose($fp_dest); return true; } else { + fclose($fp_src); throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source); } - fclose($fp_dest); } else { throw new Exception('failed to create file for writing '.$filename_dest); } - fclose($fp_src); } else { throw new Exception('failed to open file for reading '.$filename_source); } - return false; } + /** + * @param int $charval + * + * @return string + */ public static function iconv_fallback_int_utf8($charval) { if ($charval < 128) { // 0bbbbbbb @@ -695,7 +917,14 @@ class getid3_lib return $newcharstring; } - // ISO-8859-1 => UTF-8 + /** + * ISO-8859-1 => UTF-8 + * + * @param string $string + * @param bool $bom + * + * @return string + */ public static function iconv_fallback_iso88591_utf8($string, $bom=false) { if (function_exists('utf8_encode')) { return utf8_encode($string); @@ -712,7 +941,14 @@ class getid3_lib return $newcharstring; } - // ISO-8859-1 => UTF-16BE + /** + * ISO-8859-1 => UTF-16BE + * + * @param string $string + * @param bool $bom + * + * @return string + */ public static function iconv_fallback_iso88591_utf16be($string, $bom=false) { $newcharstring = ''; if ($bom) { @@ -724,7 +960,14 @@ class getid3_lib return $newcharstring; } - // ISO-8859-1 => UTF-16LE + /** + * ISO-8859-1 => UTF-16LE + * + * @param string $string + * @param bool $bom + * + * @return string + */ public static function iconv_fallback_iso88591_utf16le($string, $bom=false) { $newcharstring = ''; if ($bom) { @@ -736,12 +979,24 @@ class getid3_lib return $newcharstring; } - // ISO-8859-1 => UTF-16LE (BOM) + /** + * ISO-8859-1 => UTF-16LE (BOM) + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_iso88591_utf16($string) { return self::iconv_fallback_iso88591_utf16le($string, true); } - // UTF-8 => ISO-8859-1 + /** + * UTF-8 => ISO-8859-1 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf8_iso88591($string) { if (function_exists('utf8_decode')) { return utf8_decode($string); @@ -785,7 +1040,14 @@ class getid3_lib return $newcharstring; } - // UTF-8 => UTF-16BE + /** + * UTF-8 => UTF-16BE + * + * @param string $string + * @param bool $bom + * + * @return string + */ public static function iconv_fallback_utf8_utf16be($string, $bom=false) { $newcharstring = ''; if ($bom) { @@ -828,7 +1090,14 @@ class getid3_lib return $newcharstring; } - // UTF-8 => UTF-16LE + /** + * UTF-8 => UTF-16LE + * + * @param string $string + * @param bool $bom + * + * @return string + */ public static function iconv_fallback_utf8_utf16le($string, $bom=false) { $newcharstring = ''; if ($bom) { @@ -871,12 +1140,24 @@ class getid3_lib return $newcharstring; } - // UTF-8 => UTF-16LE (BOM) + /** + * UTF-8 => UTF-16LE (BOM) + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf8_utf16($string) { return self::iconv_fallback_utf8_utf16le($string, true); } - // UTF-16BE => UTF-8 + /** + * UTF-16BE => UTF-8 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16be_utf8($string) { if (substr($string, 0, 2) == "\xFE\xFF") { // strip BOM @@ -890,7 +1171,13 @@ class getid3_lib return $newcharstring; } - // UTF-16LE => UTF-8 + /** + * UTF-16LE => UTF-8 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16le_utf8($string) { if (substr($string, 0, 2) == "\xFF\xFE") { // strip BOM @@ -904,7 +1191,13 @@ class getid3_lib return $newcharstring; } - // UTF-16BE => ISO-8859-1 + /** + * UTF-16BE => ISO-8859-1 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16be_iso88591($string) { if (substr($string, 0, 2) == "\xFE\xFF") { // strip BOM @@ -918,7 +1211,13 @@ class getid3_lib return $newcharstring; } - // UTF-16LE => ISO-8859-1 + /** + * UTF-16LE => ISO-8859-1 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16le_iso88591($string) { if (substr($string, 0, 2) == "\xFF\xFE") { // strip BOM @@ -932,7 +1231,13 @@ class getid3_lib return $newcharstring; } - // UTF-16 (BOM) => ISO-8859-1 + /** + * UTF-16 (BOM) => ISO-8859-1 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16_iso88591($string) { $bom = substr($string, 0, 2); if ($bom == "\xFE\xFF") { @@ -943,7 +1248,13 @@ class getid3_lib return $string; } - // UTF-16 (BOM) => UTF-8 + /** + * UTF-16 (BOM) => UTF-8 + * + * @param string $string + * + * @return string + */ public static function iconv_fallback_utf16_utf8($string) { $bom = substr($string, 0, 2); if ($bom == "\xFE\xFF") { @@ -954,13 +1265,21 @@ class getid3_lib return $string; } + /** + * @param string $in_charset + * @param string $out_charset + * @param string $string + * + * @return string + * @throws Exception + */ public static function iconv_fallback($in_charset, $out_charset, $string) { if ($in_charset == $out_charset) { return $string; } - // mb_convert_encoding() availble + // mb_convert_encoding() available if (function_exists('mb_convert_encoding')) { if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) { switch ($out_charset) { @@ -972,7 +1291,7 @@ class getid3_lib } return $string; } - // iconv() availble + // iconv() available else if (function_exists('iconv')) { if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { switch ($out_charset) { @@ -1014,6 +1333,12 @@ class getid3_lib throw new Exception('PHP does not has mb_convert_encoding() or iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); } + /** + * @param mixed $data + * @param string $charset + * + * @return mixed + */ public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') { if (is_string($data)) { return self::MultiByteCharString2HTML($data, $charset); @@ -1028,6 +1353,12 @@ class getid3_lib return $data; } + /** + * @param string|int|float $string + * @param string $charset + * + * @return string + */ public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string $HTMLstring = ''; @@ -1120,8 +1451,11 @@ class getid3_lib return $HTMLstring; } - - + /** + * @param int $namecode + * + * @return string + */ public static function RGADnameLookup($namecode) { static $RGADname = array(); if (empty($RGADname)) { @@ -1133,7 +1467,11 @@ class getid3_lib return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : ''); } - + /** + * @param int $originatorcode + * + * @return string + */ public static function RGADoriginatorLookup($originatorcode) { static $RGADoriginator = array(); if (empty($RGADoriginator)) { @@ -1146,16 +1484,27 @@ class getid3_lib return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : ''); } - + /** + * @param int $rawadjustment + * @param int $signbit + * + * @return float + */ public static function RGADadjustmentLookup($rawadjustment, $signbit) { - $adjustment = $rawadjustment / 10; + $adjustment = (float) $rawadjustment / 10; if ($signbit == 1) { $adjustment *= -1; } - return (float) $adjustment; + return $adjustment; } - + /** + * @param int $namecode + * @param int $originatorcode + * @param int $replaygain + * + * @return string + */ public static function RGADgainString($namecode, $originatorcode, $replaygain) { if ($replaygain < 0) { $signbit = '1'; @@ -1171,11 +1520,21 @@ class getid3_lib return $gainstring; } + /** + * @param float $amplitude + * + * @return float + */ public static function RGADamplitude2dB($amplitude) { return 20 * log10($amplitude); } - + /** + * @param string $imgData + * @param array $imageinfo + * + * @return array|false + */ public static function GetDataImageSize($imgData, &$imageinfo=array()) { static $tempdir = ''; if (empty($tempdir)) { @@ -1210,32 +1569,21 @@ class getid3_lib return $GetDataImageSize; } + /** + * @param string $mime_type + * + * @return string + */ public static function ImageExtFromMime($mime_type) { // temporary way, works OK for now, but should be reworked in the future return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type); } - public static function ImageTypesLookup($imagetypeid) { - static $ImageTypesLookup = array(); - if (empty($ImageTypesLookup)) { - $ImageTypesLookup[1] = 'gif'; - $ImageTypesLookup[2] = 'jpeg'; - $ImageTypesLookup[3] = 'png'; - $ImageTypesLookup[4] = 'swf'; - $ImageTypesLookup[5] = 'psd'; - $ImageTypesLookup[6] = 'bmp'; - $ImageTypesLookup[7] = 'tiff (little-endian)'; - $ImageTypesLookup[8] = 'tiff (big-endian)'; - $ImageTypesLookup[9] = 'jpc'; - $ImageTypesLookup[10] = 'jp2'; - $ImageTypesLookup[11] = 'jpx'; - $ImageTypesLookup[12] = 'jb2'; - $ImageTypesLookup[13] = 'swc'; - $ImageTypesLookup[14] = 'iff'; - } - return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : ''); - } - + /** + * @param array $ThisFileInfo + * + * @return bool + */ public static function CopyTagsToComments(&$ThisFileInfo) { // Copy all entries from ['tags'] into common ['comments'] @@ -1323,7 +1671,15 @@ class getid3_lib return true; } - + /** + * @param string $key + * @param int $begin + * @param int $end + * @param string $file + * @param string $name + * + * @return string + */ public static function EmbeddedLookup($key, $begin, $end, $file, $name) { // Cached @@ -1370,6 +1726,14 @@ class getid3_lib return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); } + /** + * @param string $filename + * @param string $sourcefile + * @param bool $DieOnFailure + * + * @return bool + * @throws Exception + */ public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { global $GETID3_ERRORARRAY; @@ -1390,10 +1754,20 @@ class getid3_lib return false; } + /** + * @param string $string + * + * @return string + */ public static function trimNullByte($string) { return trim($string, "\x00"); } + /** + * @param string $path + * + * @return float|bool + */ public static function getFileSizeSyscall($path) { $filesize = false; @@ -1418,13 +1792,44 @@ class getid3_lib return $filesize; } + /** + * @param string $filename + * + * @return string|false + */ + public static function truepath($filename) { + // 2017-11-08: this could use some improvement, patches welcome + if (preg_match('#^(\\\\\\\\|//)[a-z0-9]#i', $filename, $matches)) { + // PHP's built-in realpath function does not work on UNC Windows shares + $goodpath = array(); + foreach (explode('/', str_replace('\\', '/', $filename)) as $part) { + if ($part == '.') { + continue; + } + if ($part == '..') { + if (count($goodpath)) { + array_pop($goodpath); + } else { + // cannot step above this level, already at top level + return false; + } + } else { + $goodpath[] = $part; + } + } + return implode(DIRECTORY_SEPARATOR, $goodpath); + } + return realpath($filename); + } /** - * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268) - * @param string $path A path. - * @param string $suffix If the name component ends in suffix this will also be cut off. - * @return string - */ + * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268) + * + * @param string $path A path. + * @param string $suffix If the name component ends in suffix this will also be cut off. + * + * @return string + */ public static function mb_basename($path, $suffix = null) { $splited = preg_split('#/#', rtrim($path, '/ ')); return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1); diff --git a/lib/getid3/getid3.php b/lib/getid3/getid3.php index 37bf94437ffc365b3bd3cb05df2a52426f04e213..90d7c9c2a7a036ed636147364d8f9d8e892c6e12 100644 --- a/lib/getid3/getid3.php +++ b/lib/getid3/getid3.php @@ -1,10 +1,10 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // // // // Please see readme.txt for more information // // /// @@ -26,6 +26,14 @@ if (!defined('ENT_SUBSTITUTE')) { // PHP5.3 adds ENT_IGNORE, PHP5.4 adds ENT_SUB define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8)); } +/* +https://www.getid3.org/phpBB3/viewtopic.php?t=2114 +If you are running into a the problem where filenames with special characters are being handled +incorrectly by external helper programs (e.g. metaflac), notably with the special characters removed, +and you are passing in the filename in UTF8 (typically via a HTML form), try uncommenting this line: +*/ +//setlocale(LC_CTYPE, 'en_US.UTF-8'); + // attempt to define temp dir as something flexible but reliable $temp_dir = ini_get('upload_tmp_dir'); if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) { @@ -74,66 +82,196 @@ unset($open_basedir, $temp_dir); class getID3 { - // public: Settings - public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE - public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252' - - // public: Optional tag checks - disable for speed. - public $option_tag_id3v1 = true; // Read and process ID3v1 tags - public $option_tag_id3v2 = true; // Read and process ID3v2 tags - public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags - public $option_tag_apetag = true; // Read and process APE tags - public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding - public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities - - // public: Optional tag/comment calucations - public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc - - // public: Optional handling of embedded attachments (e.g. images) - public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility - - // public: Optional calculations - public $option_md5_data = false; // Get MD5 sum of data part - slow - public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG - public $option_sha1_data = false; // Get SHA1 sum of data part - slow - public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX) - - // public: Read buffer size in bytes + /* + * Settings + */ + + /** + * CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE + * + * @var string + */ + public $encoding = 'UTF-8'; + + /** + * Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252' + * + * @var string + */ + public $encoding_id3v1 = 'ISO-8859-1'; + + /* + * Optional tag checks - disable for speed. + */ + + /** + * Read and process ID3v1 tags + * + * @var bool + */ + public $option_tag_id3v1 = true; + + /** + * Read and process ID3v2 tags + * + * @var bool + */ + public $option_tag_id3v2 = true; + + /** + * Read and process Lyrics3 tags + * + * @var bool + */ + public $option_tag_lyrics3 = true; + + /** + * Read and process APE tags + * + * @var bool + */ + public $option_tag_apetag = true; + + /** + * Copy tags to root key 'tags' and encode to $this->encoding + * + * @var bool + */ + public $option_tags_process = true; + + /** + * Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities + * + * @var bool + */ + public $option_tags_html = true; + + /* + * Optional tag/comment calculations + */ + + /** + * Calculate additional info such as bitrate, channelmode etc + * + * @var bool + */ + public $option_extra_info = true; + + /* + * Optional handling of embedded attachments (e.g. images) + */ + + /** + * Defaults to true (ATTACHMENTS_INLINE) for backward compatibility + * + * @var bool|string + */ + public $option_save_attachments = true; + + /* + * Optional calculations + */ + + /** + * Get MD5 sum of data part - slow + * + * @var bool + */ + public $option_md5_data = false; + + /** + * Use MD5 of source file if availble - only FLAC and OptimFROG + * + * @var bool + */ + public $option_md5_data_source = false; + + /** + * Get SHA1 sum of data part - slow + * + * @var bool + */ + public $option_sha1_data = false; + + /** + * Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on + * PHP_INT_MAX) + * + * @var bool|null + */ + public $option_max_2gb_check; + + /** + * Read buffer size in bytes + * + * @var int + */ public $option_fread_buffer_size = 32768; // Public variables - public $filename; // Filename of file being analysed. - public $fp; // Filepointer to file being analysed. - public $info; // Result array. + + /** + * Filename of file being analysed. + * + * @var string + */ + public $filename; + + /** + * Filepointer to file being analysed. + * + * @var resource + */ + public $fp; + + /** + * Result array. + * + * @var array + */ + public $info; + + /** + * @var string + */ public $tempdir = GETID3_TEMP_DIR; + + /** + * @var int + */ public $memory_limit = 0; - // Protected variables + /** + * @var string + */ protected $startup_error = ''; + + /** + * @var string + */ protected $startup_warning = ''; - const VERSION = '1.9.14-201703261440'; + const VERSION = '1.9.16-201810171314'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; const ATTACHMENTS_INLINE = true; - // public: constructor public function __construct() { // Check for PHP version $required_php_version = '5.3.0'; if (version_compare(PHP_VERSION, $required_php_version, '<')) { $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION."\n"; - return false; + return; } // Check memory $this->memory_limit = ini_get('memory_limit'); - if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) { + if (preg_match('#([0-9]+) ?M#i', $this->memory_limit, $matches)) { // could be stored as "16M" rather than 16777216 for example $this->memory_limit = $matches[1] * 1048576; - } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0 + } elseif (preg_match('#([0-9]+) ?G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0 // could be stored as "2G" rather than 2147483648 for example $this->memory_limit = $matches[1] * 1073741824; } @@ -226,20 +364,27 @@ class getID3 echo $this->startup_error; throw new getid3_exception($this->startup_error); } - - return true; } + /** + * @return string + */ public function version() { return self::VERSION; } + /** + * @return int + */ public function fread_buffer_size() { return $this->option_fread_buffer_size; } - - // public: setOption + /** + * @param array $optArray + * + * @return bool + */ public function setOption($optArray) { if (!is_array($optArray) || empty($optArray)) { return false; @@ -253,7 +398,14 @@ class getID3 return true; } - + /** + * @param string $filename + * @param int $filesize + * + * @return bool + * + * @throws getid3_exception + */ public function openfile($filename, $filesize=null) { try { if (!empty($this->startup_error)) { @@ -277,10 +429,10 @@ class getID3 } $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename); - $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename); + //$filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename); // open local file - //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720 + //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720 if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // great } else { @@ -338,10 +490,10 @@ class getID3 } elseif (getid3_lib::intValueSupported($real_filesize)) { unset($this->info['filesize']); fclose($this->fp); - throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org'); + 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'); } $this->info['filesize'] = $real_filesize; - $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.'); + $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.'); } } @@ -353,7 +505,15 @@ class getID3 return false; } - // public: analyze file + /** + * analyze file + * + * @param string $filename + * @param int $filesize + * @param string $original_filename + * + * @return array + */ public function analyze($filename, $filesize=null, $original_filename='') { try { if (!$this->openfile($filename, $filesize)) { @@ -504,7 +664,13 @@ class getID3 } - // private: error handling + /** + * Error handling. + * + * @param string $message + * + * @return array + */ public function error($message) { $this->CleanUp(); if (!isset($this->info['error'])) { @@ -515,14 +681,22 @@ class getID3 } - // private: warning handling + /** + * Warning handling. + * + * @param string $message + * + * @return bool + */ public function warning($message) { $this->info['warning'][] = $message; return true; } - // private: CleanUp + /** + * @return bool + */ private function CleanUp() { // remove possible empty keys @@ -569,8 +743,11 @@ class getID3 return true; } - - // return array containing information about all supported formats + /** + * Return array containing information about all supported formats. + * + * @return array + */ public function GetFileFormatArray() { static $format_info = array(); if (empty($format_info)) { @@ -591,7 +768,7 @@ class getID3 'pattern' => '^ADIF', 'group' => 'audio', 'module' => 'aac', - 'mime_type' => 'application/octet-stream', + 'mime_type' => 'audio/aac', 'fail_ape' => 'WARNING', ), @@ -609,7 +786,7 @@ class getID3 'pattern' => '^\\xFF[\\xF0-\\xF1\\xF8-\\xF9]', 'group' => 'audio', 'module' => 'aac', - 'mime_type' => 'application/octet-stream', + 'mime_type' => 'audio/aac', 'fail_ape' => 'WARNING', ), @@ -675,7 +852,7 @@ class getID3 'pattern' => '^fLaC', 'group' => 'audio', 'module' => 'flac', - 'mime_type' => 'audio/x-flac', + 'mime_type' => 'audio/flac', ), // LA - audio - Lossless Audio (LA) @@ -707,7 +884,7 @@ class getID3 'pattern' => '^MAC ', 'group' => 'audio', 'module' => 'monkey', - 'mime_type' => 'application/octet-stream', + 'mime_type' => 'audio/x-monkeys-audio', ), // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available @@ -896,7 +1073,7 @@ class getID3 'pattern' => '^(RIFF|SDSS|FORM)', 'group' => 'audio-video', 'module' => 'riff', - 'mime_type' => 'audio/x-wav', + 'mime_type' => 'audio/wav', 'fail_ape' => 'WARNING', ), @@ -1060,7 +1237,7 @@ class getID3 'pattern' => '^\\x1F\\x8B\\x08', 'group' => 'archive', 'module' => 'gzip', - 'mime_type' => 'application/x-gzip', + 'mime_type' => 'application/gzip', 'fail_id3' => 'ERROR', 'fail_ape' => 'ERROR', ), @@ -1122,8 +1299,12 @@ class getID3 return $format_info; } - - + /** + * @param string $filedata + * @param string $filename + * + * @return mixed|false + */ public function GetFileFormat(&$filedata, $filename='') { // this function will determine the format of a file based on usually // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, @@ -1161,8 +1342,12 @@ class getID3 return false; } - - // converts array to $encoding charset from $this->encoding + /** + * Converts array to $encoding charset from $this->encoding. + * + * @param array $array + * @param string $encoding + */ public function CharConvert(&$array, $encoding) { // identical encoding - end here @@ -1185,7 +1370,9 @@ class getID3 } } - + /** + * @return bool + */ public function HandleAllTags() { // key name => array (tag name, character encoding) @@ -1308,6 +1495,11 @@ class getID3 return true; } + /** + * @param string $algorithm + * + * @return array|bool + */ public function getHashdata($algorithm) { switch ($algorithm) { case 'md5': @@ -1372,7 +1564,6 @@ class getID3 } else { - $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; $VorbisCommentError = `$commandline`; @@ -1431,7 +1622,6 @@ class getID3 return true; } - public function ChannelsBitratePlaytimeCalculations() { // set channelmode on audio @@ -1496,7 +1686,9 @@ class getID3 } } - + /** + * @return bool + */ public function CalculateCompressionRatioVideo() { if (empty($this->info['video'])) { return false; @@ -1544,7 +1736,9 @@ class getID3 return true; } - + /** + * @return bool + */ public function CalculateCompressionRatioAudio() { if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) { return false; @@ -1561,11 +1755,13 @@ class getID3 return true; } - + /** + * @return bool + */ public function CalculateReplayGain() { if (isset($this->info['replay_gain'])) { if (!isset($this->info['replay_gain']['reference_volume'])) { - $this->info['replay_gain']['reference_volume'] = (double) 89.0; + $this->info['replay_gain']['reference_volume'] = 89.0; } if (isset($this->info['replay_gain']['track']['adjustment'])) { $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; @@ -1584,6 +1780,9 @@ class getID3 return true; } + /** + * @return bool + */ public function ProcessAudioStreams() { if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { if (!isset($this->info['audio']['streams'])) { @@ -1597,10 +1796,20 @@ class getID3 return true; } + /** + * @return string|bool + */ public function getid3_tempnam() { return tempnam($this->tempdir, 'gI3'); } + /** + * @param string $name + * + * @return bool + * + * @throws getid3_exception + */ public function include_module($name) { //if (!file_exists($this->include_path.'module.'.$name.'.php')) { if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) { @@ -1610,24 +1819,72 @@ class getID3 return true; } + /** + * @param string $filename + * + * @return bool + */ + public static function is_writable ($filename) { + $ret = is_writable($filename); + + if (!$ret) { + $perms = fileperms($filename); + $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002); + } + + return $ret; + } + } -abstract class getid3_handler { +abstract class getid3_handler +{ /** * @var getID3 */ protected $getid3; // pointer - protected $data_string_flag = false; // analyzing filepointer or string - protected $data_string = ''; // string to analyze - protected $data_string_position = 0; // seek position in string - protected $data_string_length = 0; // string length + /** + * Analyzing filepointer or string. + * + * @var bool + */ + protected $data_string_flag = false; - private $dependency_to = null; + /** + * String to analyze. + * + * @var string + */ + protected $data_string = ''; + /** + * Seek position in string. + * + * @var int + */ + protected $data_string_position = 0; + /** + * String length. + * + * @var int + */ + protected $data_string_length = 0; + + /** + * @var string + */ + private $dependency_to; + + /** + * getid3_handler constructor. + * + * @param getID3 $getid3 + * @param string $call_module + */ public function __construct(getID3 $getid3, $call_module=null) { $this->getid3 = $getid3; @@ -1636,12 +1893,18 @@ abstract class getid3_handler { } } - - // Analyze from file pointer + /** + * Analyze from file pointer. + * + * @return bool + */ abstract public function Analyze(); - - // Analyze from string instead + /** + * Analyze from string instead. + * + * @param string $string + */ public function AnalyzeString($string) { // Enter string mode $this->setStringMode($string); @@ -1667,12 +1930,18 @@ abstract class getid3_handler { $this->data_string_flag = false; } + /** + * @param string $string + */ public function setStringMode($string) { $this->data_string_flag = true; $this->data_string = $string; $this->data_string_length = strlen($string); } + /** + * @return int|bool + */ protected function ftell() { if ($this->data_string_flag) { return $this->data_string_position; @@ -1680,6 +1949,13 @@ abstract class getid3_handler { return ftell($this->getid3->fp); } + /** + * @param int $bytes + * + * @return string|false + * + * @throws getid3_exception + */ protected function fread($bytes) { if ($this->data_string_flag) { $this->data_string_position += $bytes; @@ -1692,7 +1968,7 @@ abstract class getid3_handler { //return fread($this->getid3->fp, $bytes); /* - * http://www.getid3.org/phpBB3/viewtopic.php?t=1930 + * https://www.getid3.org/phpBB3/viewtopic.php?t=1930 * "I found out that the root cause for the problem was how getID3 uses the PHP system function fread(). * It seems to assume that fread() would always return as many bytes as were requested. * 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. @@ -1700,6 +1976,9 @@ abstract class getid3_handler { */ $contents = ''; do { + if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) { + throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10); + } $part = fread($this->getid3->fp, $bytes); $partLength = strlen($part); $bytes -= $partLength; @@ -1708,6 +1987,14 @@ abstract class getid3_handler { return $contents; } + /** + * @param int $bytes + * @param int $whence + * + * @return int + * + * @throws getid3_exception + */ protected function fseek($bytes, $whence=SEEK_SET) { if ($this->data_string_flag) { switch ($whence) { @@ -1738,6 +2025,9 @@ abstract class getid3_handler { return fseek($this->getid3->fp, $bytes, $whence); } + /** + * @return bool + */ protected function feof() { if ($this->data_string_flag) { return $this->data_string_position >= $this->data_string_length; @@ -1745,24 +2035,53 @@ abstract class getid3_handler { return feof($this->getid3->fp); } + /** + * @param string $module + * + * @return bool + */ final protected function isDependencyFor($module) { return $this->dependency_to == $module; } + /** + * @param string $text + * + * @return bool + */ protected function error($text) { $this->getid3->info['error'][] = $text; return false; } + /** + * @param string $text + * + * @return bool + */ protected function warning($text) { return $this->getid3->warning($text); } + /** + * @param string $text + */ protected function notice($text) { // does nothing for now } + /** + * @param string $name + * @param int $offset + * @param int $length + * @param string $image_mime + * + * @return string|null + * + * @throws Exception + * @throws getid3_exception + */ public function saveAttachment($name, $offset, $length, $image_mime=null) { try { @@ -1785,7 +2104,7 @@ abstract class getid3_handler { // set up destination path $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); - if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory + if (!is_dir($dir) || !getID3::is_writable($dir)) { // check supplied directory throw new Exception('supplied path ('.$dir.') does not exist, or is not writable'); } $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : ''); @@ -1816,6 +2135,9 @@ abstract class getid3_handler { // close and remove dest file if created if (isset($fp_dest) && is_resource($fp_dest)) { fclose($fp_dest); + } + + if (isset($dest) && file_exists($dest)) { unlink($dest); } diff --git a/lib/getid3/module.archive.gzip.php b/lib/getid3/module.archive.gzip.php index 112a1e6d0977199386967d047800b3aa45bd86d3..5d730be84b755ec3c80b7c76167a172e3ca18438 100644 --- a/lib/getid3/module.archive.gzip.php +++ b/lib/getid3/module.archive.gzip.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.gzip.php // @@ -20,11 +20,20 @@ ///////////////////////////////////////////////////////////////// -class getid3_gzip extends getid3_handler { - - // public: Optional file list - disable for speed. - public $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) +class getid3_gzip extends getid3_handler +{ + /** + * Optional file list - disable for speed. + * + * Decode gzipped files, if possible, and parse recursively (.tar.gz for example). + * + * @var bool + */ + public $option_gzip_parse_contents = false; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -44,6 +53,7 @@ class getid3_gzip extends getid3_handler { $buffer = $this->fread($info['filesize']); $arr_members = explode("\x1F\x8B\x08", $buffer); + $num_members = 0; while (true) { $is_wrong_members = false; $num_members = intval(count($arr_members)); @@ -199,7 +209,7 @@ class getid3_gzip extends getid3_handler { $inflated = gzinflate($cdata); // Calculate CRC32 for inflated content - $thisInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisInfo['crc32']); + $thisInfo['crc32_valid'] = sprintf('%u', crc32($inflated)) == $thisInfo['crc32']; // determine format $formattest = substr($inflated, 0, 32774); @@ -240,13 +250,21 @@ class getid3_gzip extends getid3_handler { // unknown or unhandled format break; } + } else { + $this->warning('PHP is not compiled with gzinflate() support. Please enable PHP Zlib extension or recompile with the --with-zlib switch'); } } } return true; } - // Converts the OS type + /** + * Converts the OS type. + * + * @param string $key + * + * @return string + */ public function get_os_type($key) { static $os_type = array( '0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)', @@ -268,7 +286,13 @@ class getid3_gzip extends getid3_handler { return (isset($os_type[$key]) ? $os_type[$key] : ''); } - // Converts the eXtra FLags + /** + * Converts the eXtra FLags. + * + * @param string $key + * + * @return string + */ public function get_xflag_type($key) { static $xflag_type = array( '0' => 'unknown', diff --git a/lib/getid3/module.archive.rar.php b/lib/getid3/module.archive.rar.php index 30961fb5cb0718ebfa8c618dbce83f6ec2548c68..c8f26788c69cf58c31b19c3079276527a249f1aa 100644 --- a/lib/getid3/module.archive.rar.php +++ b/lib/getid3/module.archive.rar.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.rar.php // @@ -17,9 +17,14 @@ class getid3_rar extends getid3_handler { - + /** + * @var bool + */ public $option_use_rar_extension = false; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.archive.szip.php b/lib/getid3/module.archive.szip.php index 675172944b3578a9ea9eef0e63efec92e6a5f05c..5b0601b4c340facc3e283fd9f2d353f6d4d0fb29 100644 --- a/lib/getid3/module.archive.szip.php +++ b/lib/getid3/module.archive.szip.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.szip.php // @@ -17,7 +17,9 @@ class getid3_szip extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -30,8 +32,8 @@ class getid3_szip extends getid3_handler $info['fileformat'] = 'szip'; $info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); $info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); -$this->error('SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; while (!$this->feof()) { $NextBlockID = $this->fread(2); diff --git a/lib/getid3/module.archive.tar.php b/lib/getid3/module.archive.tar.php index 9e8d72a29773b57f0da9e2472b9379ef73e32f76..952a626e722ca0581bbd79f2d5d648519b4832d4 100644 --- a/lib/getid3/module.archive.tar.php +++ b/lib/getid3/module.archive.tar.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.tar.php // @@ -22,7 +22,9 @@ class getid3_tar extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -117,7 +119,13 @@ class getid3_tar extends getid3_handler return true; } - // Parses the file mode to file permissions + /** + * Parses the file mode to file permissions. + * + * @param int $mode + * + * @return string + */ public function display_perms($mode) { // Determine Type if ($mode & 0x1000) $type='p'; // FIFO pipe @@ -152,7 +160,13 @@ class getid3_tar extends getid3_handler return $s; } - // Converts the file type + /** + * Converts the file type. + * + * @param string $typflag + * + * @return mixed|string + */ public function get_flag_type($typflag) { static $flag_types = array( '0' => 'LF_NORMAL', diff --git a/lib/getid3/module.archive.zip.php b/lib/getid3/module.archive.zip.php index 4b7aa5835d2a3c34873539e7ac79f5353c773a38..7cb8719d2d82d53e4abd82a88fa32cf90282153d 100644 --- a/lib/getid3/module.archive.zip.php +++ b/lib/getid3/module.archive.zip.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.zip.php // @@ -17,7 +17,9 @@ class getid3_zip extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -48,7 +50,7 @@ class getid3_zip extends getid3_handler $this->fseek($info['zip']['end_central_directory']['directory_offset']); $info['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory()) { $info['zip']['central_directory'][] = $centraldirectoryentry; $info['zip']['entries_count']++; $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; @@ -129,7 +131,9 @@ class getid3_zip extends getid3_handler return true; } - + /** + * @return bool + */ public function getZIPHeaderFilepointerTopDown() { $info = &$this->getid3->info; @@ -150,7 +154,7 @@ class getid3_zip extends getid3_handler } $info['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory()) { $info['zip']['central_directory'][] = $centraldirectoryentry; $info['zip']['entries_count']++; $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; @@ -175,7 +179,9 @@ class getid3_zip extends getid3_handler return true; } - + /** + * @return bool + */ public function getZIPentriesFilepointer() { $info = &$this->getid3->info; @@ -198,7 +204,9 @@ class getid3_zip extends getid3_handler return true; } - + /** + * @return array|false + */ public function ZIPparseLocalFileHeader() { $LocalFileHeader['offset'] = $this->ftell(); @@ -265,7 +273,7 @@ class getid3_zip extends getid3_handler $DataDescriptor = $this->fread(16); $LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08" - $this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']); + $this->getid3->warning('invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature'])); $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly return false; } @@ -294,7 +302,9 @@ class getid3_zip extends getid3_handler return $LocalFileHeader; } - + /** + * @return array|false + */ public function ZIPparseCentralDirectory() { $CentralDirectory['offset'] = $this->ftell(); @@ -351,6 +361,9 @@ class getid3_zip extends getid3_handler return $CentralDirectory; } + /** + * @return array|false + */ public function ZIPparseEndOfCentralDirectory() { $EndOfCentralDirectory['offset'] = $this->ftell(); @@ -377,9 +390,15 @@ class getid3_zip extends getid3_handler return $EndOfCentralDirectory; } - + /** + * @param int $flagbytes + * @param int $compressionmethod + * + * @return array + */ public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html + $ParsedFlags = array(); $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); // 0x0002 -- see below // 0x0004 -- see below @@ -425,7 +444,11 @@ class getid3_zip extends getid3_handler return $ParsedFlags; } - + /** + * @param int $index + * + * @return string + */ public static function ZIPversionOSLookup($index) { static $ZIPversionOSLookup = array( 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', @@ -453,6 +476,11 @@ class getid3_zip extends getid3_handler return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); } + /** + * @param int $index + * + * @return string + */ public static function ZIPcompressionMethodLookup($index) { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html static $ZIPcompressionMethodLookup = array( @@ -484,6 +512,12 @@ class getid3_zip extends getid3_handler return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); } + /** + * @param int $DOSdate + * @param int $DOStime + * + * @return int + */ public static function DOStime2UNIXtime($DOSdate, $DOStime) { // wFatDate // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: diff --git a/lib/getid3/module.audio-video.asf.php b/lib/getid3/module.audio-video.asf.php index 23d3a0e680636086fdd057dbeff248d120b2810f..d42607e0cd07e5bdcef8212eebcd09451df67553 100644 --- a/lib/getid3/module.audio-video.asf.php +++ b/lib/getid3/module.audio-video.asf.php @@ -1,11 +1,10 @@ <?php ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.asf.php // @@ -16,8 +15,11 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -class getid3_asf extends getid3_handler { - +class getid3_asf extends getid3_handler +{ + /** + * @param getID3 $getid3 + */ public function __construct(getID3 $getid3) { parent::__construct($getid3); // extends getid3_handler::__construct() @@ -30,6 +32,9 @@ class getid3_asf extends getid3_handler { } } + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -83,6 +88,8 @@ class getid3_asf extends getid3_handler { $NextObjectOffset = $this->ftell(); $ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30); $offset = 0; + $thisfile_asf_streambitratepropertiesobject = array(); + $thisfile_asf_codeclistobject = array(); for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { $NextObjectGUID = substr($ASFHeaderData, $offset, 16); @@ -970,18 +977,18 @@ class getid3_asf extends getid3_handler { break; } } - if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) { + if (isset($thisfile_asf_streambitratepropertiesobject['bitrate_records_count'])) { $ASFbitrateAudio = 0; $ASFbitrateVideo = 0; - for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) { + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) { switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) { case 1: - $ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + $ASFbitrateVideo += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate']; break; case 2: - $ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + $ASFbitrateAudio += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate']; break; default: @@ -1440,6 +1447,11 @@ class getid3_asf extends getid3_handler { return true; } + /** + * @param int $CodecListType + * + * @return string + */ public static function codecListObjectTypeLookup($CodecListType) { static $lookup = array( 0x0001 => 'Video Codec', @@ -1450,6 +1462,9 @@ class getid3_asf extends getid3_handler { return (isset($lookup[$CodecListType]) ? $lookup[$CodecListType] : 'Invalid Codec Type'); } + /** + * @return array + */ public static function KnownGUIDs() { static $GUIDarray = array( 'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A', @@ -1564,6 +1579,11 @@ class getid3_asf extends getid3_handler { return $GUIDarray; } + /** + * @param string $GUIDstring + * + * @return string|false + */ public static function GUIDname($GUIDstring) { static $GUIDarray = array(); if (empty($GUIDarray)) { @@ -1572,6 +1592,11 @@ class getid3_asf extends getid3_handler { return array_search($GUIDstring, $GUIDarray); } + /** + * @param int $id + * + * @return string + */ public static function ASFIndexObjectIndexTypeLookup($id) { static $ASFIndexObjectIndexTypeLookup = array(); if (empty($ASFIndexObjectIndexTypeLookup)) { @@ -1582,6 +1607,11 @@ class getid3_asf extends getid3_handler { return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid'); } + /** + * @param string $GUIDstring + * + * @return string + */ public static function GUIDtoBytestring($GUIDstring) { // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: // first 4 bytes are in little-endian order @@ -1617,6 +1647,11 @@ class getid3_asf extends getid3_handler { return $hexbytecharstring; } + /** + * @param string $Bytestring + * + * @return string + */ public static function BytestringToGUID($Bytestring) { $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT); $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT); @@ -1642,6 +1677,12 @@ class getid3_asf extends getid3_handler { return strtoupper($GUIDstring); } + /** + * @param int $FILETIME + * @param bool $round + * + * @return float|int + */ public static function FILETIMEtoUNIXtime($FILETIME, $round=true) { // FILETIME is a 64-bit unsigned integer representing // the number of 100-nanosecond intervals since January 1, 1601 @@ -1653,6 +1694,11 @@ class getid3_asf extends getid3_handler { return ($FILETIME - 116444736000000000) / 10000000; } + /** + * @param int $WMpictureType + * + * @return string + */ public static function WMpictureTypeLookup($WMpictureType) { static $lookup = null; if ($lookup === null) { @@ -1684,6 +1730,12 @@ class getid3_asf extends getid3_handler { return (isset($lookup[$WMpictureType]) ? $lookup[$WMpictureType] : ''); } + /** + * @param string $asf_header_extension_object_data + * @param int $unhandled_sections + * + * @return array + */ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { // http://msdn.microsoft.com/en-us/library/bb643323.aspx @@ -1930,7 +1982,11 @@ class getid3_asf extends getid3_handler { return $HeaderExtensionObjectParsed; } - + /** + * @param int $id + * + * @return string + */ public static function metadataLibraryObjectDataTypeLookup($id) { static $lookup = array( 0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters @@ -1944,6 +2000,11 @@ class getid3_asf extends getid3_handler { return (isset($lookup[$id]) ? $lookup[$id] : 'invalid'); } + /** + * @param string $data + * + * @return array + */ public function ASF_WMpicture(&$data) { //typedef struct _WMPicture{ // LPWSTR pwszMIMEType; @@ -1994,14 +2055,24 @@ class getid3_asf extends getid3_handler { return $WMpicture; } - - // Remove terminator 00 00 and convert UTF-16LE to Latin-1 + /** + * Remove terminator 00 00 and convert UTF-16LE to Latin-1. + * + * @param string $string + * + * @return string + */ public static function TrimConvert($string) { return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', self::TrimTerm($string)), ' '); } - - // Remove terminator 00 00 + /** + * Remove terminator 00 00. + * + * @param string $string + * + * @return string + */ public static function TrimTerm($string) { // remove terminator, only if present (it should be, but...) if (substr($string, -2) === "\x00\x00") { diff --git a/lib/getid3/module.audio-video.bink.php b/lib/getid3/module.audio-video.bink.php index af4b4f8d4eda27ef9b189b59c6080a9c877faf0c..bd38e339596df3dc2007551abac5a07a3fb029b3 100644 --- a/lib/getid3/module.audio-video.bink.php +++ b/lib/getid3/module.audio-video.bink.php @@ -1,11 +1,10 @@ <?php ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.bink.php // @@ -17,11 +16,13 @@ class getid3_bink extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; -$this->error('Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']'); + $this->error('Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']'); $this->fseek($info['avdataoffset']); $fileTypeID = $this->fread(3); @@ -44,6 +45,9 @@ $this->error('Bink / Smacker files not properly processed by this version of get } + /** + * @return bool + */ public function ParseBink() { $info = &$this->getid3->info; $info['fileformat'] = 'bink'; @@ -61,6 +65,9 @@ $this->error('Bink / Smacker files not properly processed by this version of get return true; } + /** + * @return bool + */ public function ParseSmacker() { $info = &$this->getid3->info; $info['fileformat'] = 'smacker'; diff --git a/lib/getid3/module.audio-video.flv.php b/lib/getid3/module.audio-video.flv.php index 661c77ca1285301595c5b361cf45df4e536d563a..eb292c6136909051daeb76390e9064e2ed97d959 100644 --- a/lib/getid3/module.audio-video.flv.php +++ b/lib/getid3/module.audio-video.flv.php @@ -1,15 +1,22 @@ <?php ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.flv.php // +// module for analyzing Shockwave Flash Video files // +// dependencies: NONE // +// // +///////////////////////////////////////////////////////////////// // // // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> // // // // * version 0.1 (26 June 2005) // // // -// // // * version 0.1.1 (15 July 2005) // // minor modifications by James Heinrich <info@getid3.org> // // // @@ -43,12 +50,6 @@ // handle GETID3_FLV_VIDEO_VP6FLV_ALPHA // // improved AVCSequenceParameterSetReader::readData() // // by Xander Schouwerwou <schouwerwouØgmail*com> // -// // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.flv.php // -// module for analyzing Shockwave Flash Video files // -// dependencies: NONE // // /// ///////////////////////////////////////////////////////////////// @@ -73,12 +74,21 @@ define('H264_PROFILE_HIGH422', 122); define('H264_PROFILE_HIGH444', 144); define('H264_PROFILE_HIGH444_PREDICTIVE', 244); -class getid3_flv extends getid3_handler { - +class getid3_flv extends getid3_handler +{ const magic = 'FLV'; - public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration - + /** + * Break out of the loop if too many frames have been scanned; only scan this + * many if meta frame does not contain useful duration. + * + * @var int + */ + public $max_frames = 100000; + + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -332,7 +342,11 @@ class getid3_flv extends getid3_handler { return true; } - + /** + * @param int $id + * + * @return string|false + */ public static function audioFormatLookup($id) { static $lookup = array( 0 => 'Linear PCM, platform endian', @@ -355,6 +369,11 @@ class getid3_flv extends getid3_handler { return (isset($lookup[$id]) ? $lookup[$id] : false); } + /** + * @param int $id + * + * @return int|false + */ public static function audioRateLookup($id) { static $lookup = array( 0 => 5500, @@ -365,6 +384,11 @@ class getid3_flv extends getid3_handler { return (isset($lookup[$id]) ? $lookup[$id] : false); } + /** + * @param int $id + * + * @return int|false + */ public static function audioBitDepthLookup($id) { static $lookup = array( 0 => 8, @@ -373,6 +397,11 @@ class getid3_flv extends getid3_handler { return (isset($lookup[$id]) ? $lookup[$id] : false); } + /** + * @param int $id + * + * @return string|false + */ public static function videoCodecLookup($id) { static $lookup = array( GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', @@ -386,47 +415,84 @@ class getid3_flv extends getid3_handler { } } -class AMFStream { +class AMFStream +{ + /** + * @var string + */ public $bytes; + + /** + * @var int + */ public $pos; + /** + * @param string $bytes + */ public function __construct(&$bytes) { $this->bytes =& $bytes; $this->pos = 0; } - public function readByte() { - return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); + /** + * @return int + */ + public function readByte() { // 8-bit + return ord(substr($this->bytes, $this->pos++, 1)); } - public function readInt() { + /** + * @return int + */ + public function readInt() { // 16-bit return ($this->readByte() << 8) + $this->readByte(); } - public function readLong() { + /** + * @return int + */ + public function readLong() { // 32-bit return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); } + /** + * @return float|false + */ public function readDouble() { return getid3_lib::BigEndian2Float($this->read(8)); } + /** + * @return string + */ public function readUTF() { $length = $this->readInt(); return $this->read($length); } + /** + * @return string + */ public function readLongUTF() { $length = $this->readLong(); return $this->read($length); } + /** + * @param int $length + * + * @return string + */ public function read($length) { $val = substr($this->bytes, $this->pos, $length); $this->pos += $length; return $val; } + /** + * @return int + */ public function peekByte() { $pos = $this->pos; $val = $this->readByte(); @@ -434,6 +500,9 @@ class AMFStream { return $val; } + /** + * @return int + */ public function peekInt() { $pos = $this->pos; $val = $this->readInt(); @@ -441,6 +510,9 @@ class AMFStream { return $val; } + /** + * @return int + */ public function peekLong() { $pos = $this->pos; $val = $this->readLong(); @@ -448,6 +520,9 @@ class AMFStream { return $val; } + /** + * @return float|false + */ public function peekDouble() { $pos = $this->pos; $val = $this->readDouble(); @@ -455,6 +530,9 @@ class AMFStream { return $val; } + /** + * @return string + */ public function peekUTF() { $pos = $this->pos; $val = $this->readUTF(); @@ -462,6 +540,9 @@ class AMFStream { return $val; } + /** + * @return string + */ public function peekLongUTF() { $pos = $this->pos; $val = $this->readLongUTF(); @@ -470,13 +551,23 @@ class AMFStream { } } -class AMFReader { +class AMFReader +{ + /** + * @var AMFStream + */ public $stream; - public function __construct(&$stream) { - $this->stream =& $stream; + /** + * @param AMFStream $stream + */ + public function __construct(AMFStream $stream) { + $this->stream = $stream; } + /** + * @return mixed + */ public function readData() { $value = null; @@ -547,23 +638,36 @@ class AMFReader { return $value; } + /** + * @return float|false + */ public function readDouble() { return $this->stream->readDouble(); } + /** + * @return bool + */ public function readBoolean() { return $this->stream->readByte() == 1; } + /** + * @return string + */ public function readString() { return $this->stream->readUTF(); } + /** + * @return array + */ public function readObject() { // Get highest numerical index - ignored // $highestIndex = $this->stream->readLong(); $data = array(); + $key = null; while ($key = $this->stream->readUTF()) { $data[$key] = $this->readData(); @@ -576,15 +680,19 @@ class AMFReader { return $data; } + /** + * @return array + */ public function readMixedArray() { // Get highest numerical index - ignored $highestIndex = $this->stream->readLong(); $data = array(); + $key = null; while ($key = $this->stream->readUTF()) { if (is_numeric($key)) { - $key = (float) $key; + $key = (int) $key; } $data[$key] = $this->readData(); } @@ -597,6 +705,9 @@ class AMFReader { return $data; } + /** + * @return array + */ public function readArray() { $length = $this->stream->readLong(); $data = array(); @@ -607,34 +718,61 @@ class AMFReader { return $data; } + /** + * @return float|false + */ public function readDate() { $timestamp = $this->stream->readDouble(); $timezone = $this->stream->readInt(); return $timestamp; } + /** + * @return string + */ public function readLongString() { return $this->stream->readLongUTF(); } + /** + * @return string + */ public function readXML() { return $this->stream->readLongUTF(); } + /** + * @return array + */ public function readTypedObject() { $className = $this->stream->readUTF(); return $this->readObject(); } } -class AVCSequenceParameterSetReader { +class AVCSequenceParameterSetReader +{ + /** + * @var string + */ public $sps; public $start = 0; public $currentBytes = 0; public $currentBits = 0; + + /** + * @var int + */ public $width; + + /** + * @var int + */ public $height; + /** + * @param string $sps + */ public function __construct($sps) { $this->sps = $sps; } @@ -691,18 +829,29 @@ class AVCSequenceParameterSetReader { } } + /** + * @param int $bits + */ public function skipBits($bits) { $newBits = $this->currentBits + $bits; $this->currentBytes += (int)floor($newBits / 8); $this->currentBits = $newBits % 8; } + /** + * @return int + */ public function getBit() { $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; $this->skipBits(1); return $result; } + /** + * @param int $bits + * + * @return int + */ public function getBits($bits) { $result = 0; for ($i = 0; $i < $bits; $i++) { @@ -711,6 +860,9 @@ class AVCSequenceParameterSetReader { return $result; } + /** + * @return int + */ public function expGolombUe() { $significantBits = 0; $bit = $this->getBit(); @@ -726,6 +878,9 @@ class AVCSequenceParameterSetReader { return (1 << $significantBits) + $this->getBits($significantBits) - 1; } + /** + * @return int + */ public function expGolombSe() { $result = $this->expGolombUe(); if (($result & 0x01) == 0) { @@ -735,10 +890,16 @@ class AVCSequenceParameterSetReader { } } + /** + * @return int + */ public function getWidth() { return $this->width; } + /** + * @return int + */ public function getHeight() { return $this->height; } diff --git a/lib/getid3/module.audio-video.matroska.php b/lib/getid3/module.audio-video.matroska.php index 825a22e1926b4612036de497b37bfbf981ec9a34..b2b187b6f174fb9dc40fa2380fb50c5e54d547d8 100644 --- a/lib/getid3/module.audio-video.matroska.php +++ b/lib/getid3/module.audio-video.matroska.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.matriska.php // @@ -72,7 +72,7 @@ define('EBML_ID_FILEREFERRAL', 0x0675); // [46][75] -- define('EBML_ID_FILEDESCRIPTION', 0x067E); // [46][7E] -- A human-friendly name for the attached file. define('EBML_ID_FILEUID', 0x06AE); // [46][AE] -- Unique ID representing the file, as random as possible. define('EBML_ID_CONTENTENCALGO', 0x07E1); // [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values: -define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with. +define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the data was encrypted with. define('EBML_ID_CONTENTSIGNATURE', 0x07E3); // [47][E3] -- A cryptographic signature of the contents. define('EBML_ID_CONTENTSIGKEYID', 0x07E4); // [47][E4] -- This is the ID of the private key the data was signed with. define('EBML_ID_CONTENTSIGALGO', 0x07E5); // [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: @@ -215,17 +215,33 @@ define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- */ class getid3_matroska extends getid3_handler { - // public options - public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE] - public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE] - - // private parser settings/placeholders + /** + * If true, do not return information about CLUSTER chunks, since there's a lot of them + * and they're not usually useful [default: TRUE]. + * + * @var bool + */ + public static $hide_clusters = true; + + /** + * True to parse the whole file, not only header [default: FALSE]. + * + * @var bool + */ + public static $parse_whole_file = false; + + /* + * Private parser settings/placeholders. + */ private $EBMLbuffer = ''; private $EBMLbuffer_offset = 0; private $EBMLbuffer_length = 0; private $current_offset = 0; private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID); + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -366,8 +382,8 @@ class getid3_matroska extends getid3_handler if (!empty($getid3_temp->info[$header_data_key])) { $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key]; if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { - foreach ($getid3_temp->info['audio'] as $key => $value) { - $track_info[$key] = $value; + foreach ($getid3_temp->info['audio'] as $sub_key => $value) { + $track_info[$sub_key] = $value; } } } @@ -421,8 +437,8 @@ class getid3_matroska extends getid3_handler if (!empty($getid3_temp->info['ogg'])) { $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg']; if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { - foreach ($getid3_temp->info['audio'] as $key => $value) { - $track_info[$key] = $value; + foreach ($getid3_temp->info['audio'] as $sub_key => $value) { + $track_info[$sub_key] = $value; } } } @@ -449,9 +465,9 @@ class getid3_matroska extends getid3_handler getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']); - foreach ($parsed as $key => $value) { - if ($key != 'raw') { - $track_info[$key] = $value; + foreach ($parsed as $sub_key => $value) { + if ($sub_key != 'raw') { + $track_info[$sub_key] = $value; } } $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; @@ -496,6 +512,9 @@ class getid3_matroska extends getid3_handler return true; } + /** + * @param array $info + */ private function parseEBML(&$info) { // http://www.matroska.org/technical/specs/index.html#EBMLBasics $this->current_offset = $info['avdataoffset']; @@ -1228,6 +1247,11 @@ class getid3_matroska extends getid3_handler } } + /** + * @param int $min_data + * + * @return bool + */ private function EnsureBufferHasEnoughData($min_data=1024) { if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) { $read_bytes = max($min_data, $this->getid3->fread_buffer_size()); @@ -1249,6 +1273,9 @@ class getid3_matroska extends getid3_handler return true; } + /** + * @return int|float|false + */ private function readEBMLint() { $actual_offset = $this->current_offset - $this->EBMLbuffer_offset; @@ -1281,6 +1308,12 @@ class getid3_matroska extends getid3_handler return $int_value; } + /** + * @param int $length + * @param bool $check_buffer + * + * @return string|false + */ private function readEBMLelementData($length, $check_buffer=false) { if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) { return false; @@ -1290,6 +1323,13 @@ class getid3_matroska extends getid3_handler return $data; } + /** + * @param array $element + * @param int $parent_end + * @param array|bool $get_data + * + * @return bool + */ private function getEBMLelement(&$element, $parent_end, $get_data=false) { if ($this->current_offset >= $parent_end) { return false; @@ -1326,6 +1366,11 @@ class getid3_matroska extends getid3_handler return true; } + /** + * @param string $type + * @param int $line + * @param array $element + */ private function unhandledElement($type, $line, $element) { // warn only about unknown and missed elements, not about unuseful if (!in_array($element['id'], $this->unuseful_elements)) { @@ -1338,6 +1383,11 @@ class getid3_matroska extends getid3_handler } } + /** + * @param array $SimpleTagArray + * + * @return bool + */ private function ExtractCommentsSimpleTag($SimpleTagArray) { if (!empty($SimpleTagArray['SimpleTag'])) { foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) { @@ -1353,6 +1403,11 @@ class getid3_matroska extends getid3_handler return true; } + /** + * @param int $parent_end + * + * @return array + */ private function HandleEMBLSimpleTag($parent_end) { $simpletag_entry = array(); @@ -1383,6 +1438,13 @@ class getid3_matroska extends getid3_handler return $simpletag_entry; } + /** + * @param array $element + * @param int $block_type + * @param array $info + * + * @return array + */ private function HandleEMBLClusterBlock($element, $block_type, &$info) { // http://www.matroska.org/technical/specs/index.html#block_structure // http://www.matroska.org/technical/specs/index.html#simpleblock_structure @@ -1446,6 +1508,11 @@ class getid3_matroska extends getid3_handler return $block_data; } + /** + * @param string $EBMLstring + * + * @return int|float|false + */ private static function EBML2Int($EBMLstring) { // http://matroska.org/specs/ @@ -1488,12 +1555,22 @@ class getid3_matroska extends getid3_handler return getid3_lib::BigEndian2Int($EBMLstring); } + /** + * @param int $EBMLdatestamp + * + * @return float + */ private static function EBMLdate2unix($EBMLdatestamp) { // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC return round(($EBMLdatestamp / 1000000000) + 978307200); } + /** + * @param int $target_type + * + * @return string|int + */ public static function TargetTypeValue($target_type) { // http://www.matroska.org/technical/specs/tagging/index.html static $TargetTypeValue = array(); @@ -1509,6 +1586,11 @@ class getid3_matroska extends getid3_handler return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type); } + /** + * @param int $lacingtype + * + * @return string|int + */ public static function BlockLacingType($lacingtype) { // http://matroska.org/technical/specs/index.html#block_structure static $BlockLacingType = array(); @@ -1521,6 +1603,11 @@ class getid3_matroska extends getid3_handler return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype); } + /** + * @param string $codecid + * + * @return string + */ public static function CodecIDtoCommonName($codecid) { // http://www.matroska.org/technical/specs/codecid/index.html static $CodecIDlist = array(); @@ -1557,6 +1644,11 @@ class getid3_matroska extends getid3_handler return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid); } + /** + * @param int $value + * + * @return string + */ private static function EBMLidName($value) { static $EBMLidList = array(); if (empty($EBMLidList)) { @@ -1755,6 +1847,11 @@ class getid3_matroska extends getid3_handler return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value)); } + /** + * @param int $value + * + * @return string + */ public static function displayUnit($value) { // http://www.matroska.org/technical/specs/index.html#DisplayUnit static $units = array( @@ -1766,8 +1863,14 @@ class getid3_matroska extends getid3_handler return (isset($units[$value]) ? $units[$value] : 'unknown'); } + /** + * @param array $streams + * + * @return array + */ private static function getDefaultStreamInfo($streams) { + $stream = array(); foreach (array_reverse($streams) as $stream) { if ($stream['default']) { break; diff --git a/lib/getid3/module.audio-video.mpeg.php b/lib/getid3/module.audio-video.mpeg.php index 044481fbd561913dee0e950b2d913c4220b4971b..f27cd4b3af88cd89d424858b51dd625fac63178d 100644 --- a/lib/getid3/module.audio-video.mpeg.php +++ b/lib/getid3/module.audio-video.mpeg.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.mpeg.php // @@ -16,7 +16,8 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); -class getid3_mpeg extends getid3_handler { +class getid3_mpeg extends getid3_handler +{ const START_CODE_BASE = "\x00\x00\x01"; const VIDEO_PICTURE_START = "\x00\x00\x01\x00"; @@ -28,7 +29,9 @@ class getid3_mpeg extends getid3_handler { const VIDEO_GROUP_START = "\x00\x00\x01\xB8"; const AUDIO_START = "\x00\x00\x01\xC0"; - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -122,8 +125,8 @@ class getid3_mpeg extends getid3_handler { } } - $info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); - $info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); + $info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); // may be overridden later if file turns out to be MPEG-2 + $info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']); // may be overridden later if file turns out to be MPEG-2 $info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']); if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits = VBR //$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files'); @@ -169,6 +172,16 @@ class getid3_mpeg extends getid3_handler { $info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence']; $info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence']; $info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']); + + if (isset($info['mpeg']['video']['raw']['aspect_ratio_information'])) { + // MPEG-2 defines the aspect ratio flag differently from MPEG-1, but the MPEG-2 extension start code may occur after we've already looked up the aspect ratio assuming it was MPEG-1, so re-lookup assuming MPEG-2 + // This must be done after the extended size is known, so the display aspect ratios can be converted to pixel aspect ratios. + $info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information'], 2, $info['video']['resolution_x'], $info['video']['resolution_y']); + $info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information'], 2); + $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio']; + $info['video']['pixel_aspect_ratio_text'] = $info['mpeg']['video']['pixel_aspect_ratio_text']; + } + break; case 2: // 0010 Sequence Display Extension ID @@ -258,7 +271,7 @@ class getid3_mpeg extends getid3_handler { case 0xB8: // group_of_pictures_header $GOPcounter++; - if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') { + if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) { $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); // 27 bits needed for group_of_pictures_header $bitstreamoffset = 0; @@ -274,7 +287,7 @@ class getid3_mpeg extends getid3_handler { $GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: closed_gop $GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: broken_link - $time_code_separator = ($GOPheader['drop_frame_flag'] ? ';' : ':'); // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF" + $time_code_separator = ($GOPheader['drop_frame_flag'] ? ';' : ':'); // While non-drop time code is displayed with colons separating the digit pairs "HH:MM:SS:FF" drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs "HH;MM;SS;FF", "HH.MM.SS.FF" $GOPheader['time_code'] = sprintf('%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']); $info['mpeg']['group_of_pictures'][] = $GOPheader; @@ -502,6 +515,14 @@ echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($v return true; } + /** + * @param string $bitstream + * @param int $bitstreamoffset + * @param int $bits_to_read + * @param bool $return_singlebit_as_boolean + * + * @return bool|float|int + */ private function readBitsFromStream(&$bitstream, &$bitstreamoffset, $bits_to_read, $return_singlebit_as_boolean=true) { $return = bindec(substr($bitstream, $bitstreamoffset, $bits_to_read)); $bitstreamoffset += $bits_to_read; @@ -511,7 +532,12 @@ echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($v return $return; } - + /** + * @param int $VideoBitrate + * @param int $AudioBitrate + * + * @return float|int + */ public static function systemNonOverheadPercentage($VideoBitrate, $AudioBitrate) { $OverheadPercentage = 0; @@ -563,44 +589,93 @@ echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($v return $OverheadPercentage; } - + /** + * @param int $rawframerate + * + * @return float + */ public static function videoFramerateLookup($rawframerate) { $lookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60); - return (isset($lookup[$rawframerate]) ? (float) $lookup[$rawframerate] : (float) 0); + return (float) (isset($lookup[$rawframerate]) ? $lookup[$rawframerate] : 0); } - public static function videoAspectRatioLookup($rawaspectratio) { - $lookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0); - return (isset($lookup[$rawaspectratio]) ? (float) $lookup[$rawaspectratio] : (float) 0); + /** + * @param int $rawaspectratio + * @param int $mpeg_version + * @param int $width + * @param int $height + * + * @return float + */ + public static function videoAspectRatioLookup($rawaspectratio, $mpeg_version=1, $width=0, $height=0) { + $lookup = array( + 1 => array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0), + 2 => array(0, 1, 1.3333, 1.7778, 2.2100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + ); + $ratio = (float) (isset($lookup[$mpeg_version][$rawaspectratio]) ? $lookup[$mpeg_version][$rawaspectratio] : 0); + if ($mpeg_version == 2 && $ratio != 1) { + // Calculate pixel aspect ratio from MPEG-2 display aspect ratio + $ratio = $ratio * $height / $width; + } + return $ratio; } - public static function videoAspectRatioTextLookup($rawaspectratio) { - $lookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved'); - return (isset($lookup[$rawaspectratio]) ? $lookup[$rawaspectratio] : ''); + /** + * @param int $rawaspectratio + * @param int $mpeg_version + * + * @return string + */ + public static function videoAspectRatioTextLookup($rawaspectratio, $mpeg_version=1) { + $lookup = array( + 1 => array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved'), + 2 => array('forbidden', 'square pixels', '4:3', '16:9', '2.21:1', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved', 'reserved'), // http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html + ); + return (isset($lookup[$mpeg_version][$rawaspectratio]) ? $lookup[$mpeg_version][$rawaspectratio] : ''); } + /** + * @param int $video_format + * + * @return string + */ public static function videoFormatTextLookup($video_format) { // ISO/IEC 13818-2, section 6.3.6, Table 6-6. Meaning of video_format $lookup = array('component', 'PAL', 'NTSC', 'SECAM', 'MAC', 'Unspecified video format', 'reserved(6)', 'reserved(7)'); return (isset($lookup[$video_format]) ? $lookup[$video_format] : ''); } + /** + * @param int $scalable_mode + * + * @return string + */ public static function scalableModeTextLookup($scalable_mode) { // ISO/IEC 13818-2, section 6.3.8, Table 6-10. Definition of scalable_mode $lookup = array('data partitioning', 'spatial scalability', 'SNR scalability', 'temporal scalability'); return (isset($lookup[$scalable_mode]) ? $lookup[$scalable_mode] : ''); } + /** + * @param int $picture_structure + * + * @return string + */ public static function pictureStructureTextLookup($picture_structure) { // ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure $lookup = array('reserved', 'Top Field', 'Bottom Field', 'Frame picture'); return (isset($lookup[$picture_structure]) ? $lookup[$picture_structure] : ''); } + /** + * @param int $chroma_format + * + * @return string + */ public static function chromaFormatTextLookup($chroma_format) { // ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure $lookup = array('reserved', '4:2:0', '4:2:2', '4:4:4'); return (isset($lookup[$chroma_format]) ? $lookup[$chroma_format] : ''); } -} \ No newline at end of file +} diff --git a/lib/getid3/module.audio-video.nsv.php b/lib/getid3/module.audio-video.nsv.php index eab601b779cb8f1099fdfccbba3a749e23f97696..68a2ca1bd7ed224046ff4e644aa12b5351355907 100644 --- a/lib/getid3/module.audio-video.nsv.php +++ b/lib/getid3/module.audio-video.nsv.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.nsv.php // @@ -17,7 +17,9 @@ class getid3_nsv extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -59,6 +61,11 @@ class getid3_nsv extends getid3_handler return true; } + /** + * @param int $fileoffset + * + * @return bool + */ public function getNSVsHeaderFilepointer($fileoffset) { $info = &$this->getid3->info; $this->fseek($fileoffset); @@ -132,6 +139,12 @@ class getid3_nsv extends getid3_handler return true; } + /** + * @param int $fileoffset + * @param bool $getTOCoffsets + * + * @return bool + */ public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) { $info = &$this->getid3->info; $this->fseek($fileoffset); @@ -205,18 +218,22 @@ class getid3_nsv extends getid3_handler return true; } - + /** + * @param int $framerateindex + * + * @return float|false + */ public static function NSVframerateLookup($framerateindex) { if ($framerateindex <= 127) { return (float) $framerateindex; } static $NSVframerateLookup = array(); if (empty($NSVframerateLookup)) { - $NSVframerateLookup[129] = (float) 29.970; - $NSVframerateLookup[131] = (float) 23.976; - $NSVframerateLookup[133] = (float) 14.985; - $NSVframerateLookup[197] = (float) 59.940; - $NSVframerateLookup[199] = (float) 47.952; + $NSVframerateLookup[129] = 29.970; + $NSVframerateLookup[131] = 23.976; + $NSVframerateLookup[133] = 14.985; + $NSVframerateLookup[197] = 59.940; + $NSVframerateLookup[199] = 47.952; } return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false); } diff --git a/lib/getid3/module.audio-video.quicktime.php b/lib/getid3/module.audio-video.quicktime.php index 2ae7c74921c90cbb26b5539f28240004fea71487..5c791e32a5b5343a38e7bd8ce39dc008383e7b12 100644 --- a/lib/getid3/module.audio-video.quicktime.php +++ b/lib/getid3/module.audio-video.quicktime.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.quicktime.php // @@ -24,6 +24,9 @@ class getid3_quicktime extends getid3_handler public $ReturnAtomData = true; public $ParseAllPossibleAtoms = false; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -162,6 +165,9 @@ class getid3_quicktime extends getid3_handler if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) { $info['audio']['bitrate'] = $info['bitrate']; } + if (!empty($info['bitrate']) && !empty($info['audio']['bitrate']) && empty($info['video']['bitrate']) && !empty($info['video']['frame_rate']) && !empty($info['video']['resolution_x']) && ($info['bitrate'] > $info['audio']['bitrate'])) { + $info['video']['bitrate'] = $info['bitrate'] - $info['audio']['bitrate']; + } if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) { foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) { $samples_per_second = $samples_count / $info['playtime_seconds']; @@ -193,318 +199,353 @@ class getid3_quicktime extends getid3_handler if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) { $info['video']['dataformat'] = 'quicktime'; } + if (isset($info['video']) && ($info['mime_type'] == 'audio/mp4') && empty($info['video']['resolution_x']) && empty($info['video']['resolution_y'])) { + unset($info['video']); + } return true; } + /** + * @param string $atomname + * @param int $atomsize + * @param string $atom_data + * @param int $baseoffset + * @param array $atomHierarchy + * @param bool $ParseAllPossibleAtoms + * + * @return array|false + */ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm // https://code.google.com/p/mp4v2/wiki/iTunesMetadata $info = &$this->getid3->info; - $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717 + $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see https://www.getid3.org/phpBB3/viewtopic.php?t=1717 array_push($atomHierarchy, $atomname); $atom_structure['hierarchy'] = implode(' ', $atomHierarchy); $atom_structure['name'] = $atomname; $atom_structure['size'] = $atomsize; $atom_structure['offset'] = $baseoffset; - switch ($atomname) { - case 'moov': // MOVie container atom - case 'trak': // TRAcK container atom - case 'clip': // CLIPping container atom - case 'matt': // track MATTe container atom - case 'edts': // EDiTS container atom - case 'tref': // Track REFerence container atom - case 'mdia': // MeDIA container atom - case 'minf': // Media INFormation container atom - case 'dinf': // Data INFormation container atom - case 'udta': // User DaTA container atom - case 'cmov': // Compressed MOVie container atom - case 'rmra': // Reference Movie Record Atom - case 'rmda': // Reference Movie Descriptor Atom - case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; + if (substr($atomname, 0, 3) == "\x00\x00\x00") { + // https://github.com/JamesHeinrich/getID3/issues/139 + $atomname = getid3_lib::BigEndian2Int($atomname); + $atom_structure['name'] = $atomname; + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + } else { + switch ($atomname) { + case 'moov': // MOVie container atom + case 'trak': // TRAcK container atom + case 'clip': // CLIPping container atom + case 'matt': // track MATTe container atom + case 'edts': // EDiTS container atom + case 'tref': // Track REFerence container atom + case 'mdia': // MeDIA container atom + case 'minf': // Media INFormation container atom + case 'dinf': // Data INFormation container atom + case 'udta': // User DaTA container atom + case 'cmov': // Compressed MOVie container atom + case 'rmra': // Reference Movie Record Atom + case 'rmda': // Reference Movie Descriptor Atom + case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; - case 'ilst': // Item LiST container atom - if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) { - // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted - $allnumericnames = true; - foreach ($atom_structure['subatoms'] as $subatomarray) { - if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) { - $allnumericnames = false; - break; - } - } - if ($allnumericnames) { - $newData = array(); + case 'ilst': // Item LiST container atom + if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) { + // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted + $allnumericnames = true; foreach ($atom_structure['subatoms'] as $subatomarray) { - foreach ($subatomarray['subatoms'] as $newData_subatomarray) { - unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); - $newData[$subatomarray['name']] = $newData_subatomarray; + if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) { + $allnumericnames = false; break; } } - $atom_structure['data'] = $newData; - unset($atom_structure['subatoms']); + if ($allnumericnames) { + $newData = array(); + foreach ($atom_structure['subatoms'] as $subatomarray) { + foreach ($subatomarray['subatoms'] as $newData_subatomarray) { + unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); + $newData[$subatomarray['name']] = $newData_subatomarray; + break; + } + } + $atom_structure['data'] = $newData; + unset($atom_structure['subatoms']); + } } - } - break; - - case "\x00\x00\x00\x01": - case "\x00\x00\x00\x02": - case "\x00\x00\x00\x03": - case "\x00\x00\x00\x04": - case "\x00\x00\x00\x05": - $atomname = getid3_lib::BigEndian2Int($atomname); - $atom_structure['name'] = $atomname; - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; + break; - case 'stbl': // Sample TaBLe container atom - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - $isVideo = false; - $framerate = 0; - $framecount = 0; - foreach ($atom_structure['subatoms'] as $key => $value_array) { - if (isset($value_array['sample_description_table'])) { - foreach ($value_array['sample_description_table'] as $key2 => $value_array2) { - if (isset($value_array2['data_format'])) { - switch ($value_array2['data_format']) { - case 'avc1': - case 'mp4v': - // video data - $isVideo = true; - break; - case 'mp4a': - // audio data - break; + case 'stbl': // Sample TaBLe container atom + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + $isVideo = false; + $framerate = 0; + $framecount = 0; + foreach ($atom_structure['subatoms'] as $key => $value_array) { + if (isset($value_array['sample_description_table'])) { + foreach ($value_array['sample_description_table'] as $key2 => $value_array2) { + if (isset($value_array2['data_format'])) { + switch ($value_array2['data_format']) { + case 'avc1': + case 'mp4v': + // video data + $isVideo = true; + break; + case 'mp4a': + // audio data + break; + } } } - } - } elseif (isset($value_array['time_to_sample_table'])) { - foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) { - if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) { - $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); - $framecount = $value_array2['sample_count']; + } elseif (isset($value_array['time_to_sample_table'])) { + foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) { + if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) { + $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); + $framecount = $value_array2['sample_count']; + } } } } - } - if ($isVideo && $framerate) { - $info['quicktime']['video']['frame_rate'] = $framerate; - $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; - } - if ($isVideo && $framecount) { - $info['quicktime']['video']['frame_count'] = $framecount; - } - break; - + if ($isVideo && $framerate) { + $info['quicktime']['video']['frame_rate'] = $framerate; + $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; + } + if ($isVideo && $framecount) { + $info['quicktime']['video']['frame_count'] = $framecount; + } + break; - case "\xA9".'alb': // ALBum - case "\xA9".'ART': // - case "\xA9".'art': // ARTist - case "\xA9".'aut': // - case "\xA9".'cmt': // CoMmenT - case "\xA9".'com': // COMposer - case "\xA9".'cpy': // - case "\xA9".'day': // content created year - case "\xA9".'dir': // - case "\xA9".'ed1': // - case "\xA9".'ed2': // - case "\xA9".'ed3': // - case "\xA9".'ed4': // - case "\xA9".'ed5': // - case "\xA9".'ed6': // - case "\xA9".'ed7': // - case "\xA9".'ed8': // - case "\xA9".'ed9': // - case "\xA9".'enc': // - case "\xA9".'fmt': // - case "\xA9".'gen': // GENre - case "\xA9".'grp': // GRouPing - case "\xA9".'hst': // - case "\xA9".'inf': // - case "\xA9".'lyr': // LYRics - case "\xA9".'mak': // - case "\xA9".'mod': // - case "\xA9".'nam': // full NAMe - case "\xA9".'ope': // - case "\xA9".'PRD': // - case "\xA9".'prf': // - case "\xA9".'req': // - case "\xA9".'src': // - case "\xA9".'swr': // - case "\xA9".'too': // encoder - case "\xA9".'trk': // TRacK - case "\xA9".'url': // - case "\xA9".'wrn': // - case "\xA9".'wrt': // WRiTer - case '----': // itunes specific - case 'aART': // Album ARTist - case 'akID': // iTunes store account type - case 'apID': // Purchase Account - case 'atID': // - case 'catg': // CaTeGory - case 'cmID': // - case 'cnID': // - case 'covr': // COVeR artwork - case 'cpil': // ComPILation - case 'cprt': // CoPyRighT - case 'desc': // DESCription - case 'disk': // DISK number - case 'egid': // Episode Global ID - case 'geID': // - case 'gnre': // GeNRE - case 'hdvd': // HD ViDeo - case 'keyw': // KEYWord - case 'ldes': // Long DEScription - case 'pcst': // PodCaST - case 'pgap': // GAPless Playback - case 'plID': // - case 'purd': // PURchase Date - case 'purl': // Podcast URL - case 'rati': // - case 'rndu': // - case 'rpdu': // - case 'rtng': // RaTiNG - case 'sfID': // iTunes store country - case 'soaa': // SOrt Album Artist - case 'soal': // SOrt ALbum - case 'soar': // SOrt ARtist - case 'soco': // SOrt COmposer - case 'sonm': // SOrt NaMe - case 'sosn': // SOrt Show Name - case 'stik': // - case 'tmpo': // TeMPO (BPM) - case 'trkn': // TRacK Number - case 'tven': // tvEpisodeID - case 'tves': // TV EpiSode - case 'tvnn': // TV Network Name - case 'tvsh': // TV SHow Name - case 'tvsn': // TV SeasoN - if ($atom_parent == 'udta') { - // User data atom handler - $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); - $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); - $atom_structure['data'] = substr($atom_data, 4); - $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); - if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { - $info['comments']['language'][] = $atom_structure['language']; - } - } else { - // Apple item list box atom handler - $atomoffset = 0; - if (substr($atom_data, 2, 2) == "\x10\xB5") { - // not sure what it means, but observed on iPhone4 data. - // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data - while ($atomoffset < strlen($atom_data)) { - $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2)); - $boxsmalltype = substr($atom_data, $atomoffset + 2, 2); - $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize); - if ($boxsmallsize <= 1) { - $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); - $atom_structure['data'] = null; - $atomoffset = strlen($atom_data); - break; - } - switch ($boxsmalltype) { - case "\x10\xB5": - $atom_structure['data'] = $boxsmalldata; - break; - default: - $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset); - $atom_structure['data'] = $atom_data; - break; - } - $atomoffset += (4 + $boxsmallsize); + case "\xA9".'alb': // ALBum + case "\xA9".'ART': // + case "\xA9".'art': // ARTist + case "\xA9".'aut': // + case "\xA9".'cmt': // CoMmenT + case "\xA9".'com': // COMposer + case "\xA9".'cpy': // + case "\xA9".'day': // content created year + case "\xA9".'dir': // + case "\xA9".'ed1': // + case "\xA9".'ed2': // + case "\xA9".'ed3': // + case "\xA9".'ed4': // + case "\xA9".'ed5': // + case "\xA9".'ed6': // + case "\xA9".'ed7': // + case "\xA9".'ed8': // + case "\xA9".'ed9': // + case "\xA9".'enc': // + case "\xA9".'fmt': // + case "\xA9".'gen': // GENre + case "\xA9".'grp': // GRouPing + case "\xA9".'hst': // + case "\xA9".'inf': // + case "\xA9".'lyr': // LYRics + case "\xA9".'mak': // + case "\xA9".'mod': // + case "\xA9".'nam': // full NAMe + case "\xA9".'ope': // + case "\xA9".'PRD': // + case "\xA9".'prf': // + case "\xA9".'req': // + case "\xA9".'src': // + case "\xA9".'swr': // + case "\xA9".'too': // encoder + case "\xA9".'trk': // TRacK + case "\xA9".'url': // + case "\xA9".'wrn': // + case "\xA9".'wrt': // WRiTer + case '----': // itunes specific + case 'aART': // Album ARTist + case 'akID': // iTunes store account type + case 'apID': // Purchase Account + case 'atID': // + case 'catg': // CaTeGory + case 'cmID': // + case 'cnID': // + case 'covr': // COVeR artwork + case 'cpil': // ComPILation + case 'cprt': // CoPyRighT + case 'desc': // DESCription + case 'disk': // DISK number + case 'egid': // Episode Global ID + case 'geID': // + case 'gnre': // GeNRE + case 'hdvd': // HD ViDeo + case 'keyw': // KEYWord + case 'ldes': // Long DEScription + case 'pcst': // PodCaST + case 'pgap': // GAPless Playback + case 'plID': // + case 'purd': // PURchase Date + case 'purl': // Podcast URL + case 'rati': // + case 'rndu': // + case 'rpdu': // + case 'rtng': // RaTiNG + case 'sfID': // iTunes store country + case 'soaa': // SOrt Album Artist + case 'soal': // SOrt ALbum + case 'soar': // SOrt ARtist + case 'soco': // SOrt COmposer + case 'sonm': // SOrt NaMe + case 'sosn': // SOrt Show Name + case 'stik': // + case 'tmpo': // TeMPO (BPM) + case 'trkn': // TRacK Number + case 'tven': // tvEpisodeID + case 'tves': // TV EpiSode + case 'tvnn': // TV Network Name + case 'tvsh': // TV SHow Name + case 'tvsn': // TV SeasoN + if ($atom_parent == 'udta') { + // User data atom handler + $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); + $atom_structure['data'] = substr($atom_data, 4); + + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; } } else { - while ($atomoffset < strlen($atom_data)) { - $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4)); - $boxtype = substr($atom_data, $atomoffset + 4, 4); - $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); - if ($boxsize <= 1) { - $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); - $atom_structure['data'] = null; - $atomoffset = strlen($atom_data); - break; + // Apple item list box atom handler + $atomoffset = 0; + if (substr($atom_data, 2, 2) == "\x10\xB5") { + // not sure what it means, but observed on iPhone4 data. + // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data + while ($atomoffset < strlen($atom_data)) { + $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2)); + $boxsmalltype = substr($atom_data, $atomoffset + 2, 2); + $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize); + if ($boxsmallsize <= 1) { + $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); + $atom_structure['data'] = null; + $atomoffset = strlen($atom_data); + break; + } + switch ($boxsmalltype) { + case "\x10\xB5": + $atom_structure['data'] = $boxsmalldata; + break; + default: + $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset); + $atom_structure['data'] = $atom_data; + break; + } + $atomoffset += (4 + $boxsmallsize); } - $atomoffset += $boxsize; - - switch ($boxtype) { - case 'mean': - case 'name': - $atom_structure[$boxtype] = substr($boxdata, 4); + } else { + while ($atomoffset < strlen($atom_data)) { + $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4)); + $boxtype = substr($atom_data, $atomoffset + 4, 4); + $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); + if ($boxsize <= 1) { + $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); + $atom_structure['data'] = null; + $atomoffset = strlen($atom_data); break; + } + $atomoffset += $boxsize; + + switch ($boxtype) { + case 'mean': + case 'name': + $atom_structure[$boxtype] = substr($boxdata, 4); + break; - case 'data': - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); - switch ($atom_structure['flags_raw']) { - case 0: // data flag - case 21: // tmpo/cpil flag - switch ($atomname) { - case 'cpil': - case 'hdvd': - case 'pcst': - case 'pgap': - // 8-bit integer (boolean) - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - break; - - case 'tmpo': - // 16-bit integer - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); - break; - - case 'disk': - case 'trkn': - // binary - $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); - $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); - $atom_structure['data'] = empty($num) ? '' : $num; - $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total; - break; - - case 'gnre': - // enum - $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); - $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); - break; - - case 'rtng': - // 8-bit integer - $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); - break; - - case 'stik': - // 8-bit integer (enum) - $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); - break; - - case 'sfID': - // 32-bit integer - $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); - $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); - break; - - case 'egid': - case 'purl': - $atom_structure['data'] = substr($boxdata, 8); - break; - - case 'plID': - // 64-bit integer - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8)); - break; - - case 'covr': - $atom_structure['data'] = substr($boxdata, 8); + case 'data': + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); + switch ($atom_structure['flags_raw']) { + case 0: // data flag + case 21: // tmpo/cpil flag + switch ($atomname) { + case 'cpil': + case 'hdvd': + case 'pcst': + case 'pgap': + // 8-bit integer (boolean) + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + break; + + case 'tmpo': + // 16-bit integer + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); + break; + + case 'disk': + case 'trkn': + // binary + $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); + $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); + $atom_structure['data'] = empty($num) ? '' : $num; + $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total; + break; + + case 'gnre': + // enum + $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); + break; + + case 'rtng': + // 8-bit integer + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); + break; + + case 'stik': + // 8-bit integer (enum) + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); + break; + + case 'sfID': + // 32-bit integer + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); + break; + + case 'egid': + case 'purl': + $atom_structure['data'] = substr($boxdata, 8); + break; + + case 'plID': + // 64-bit integer + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8)); + break; + + case 'covr': + $atom_structure['data'] = substr($boxdata, 8); + // not a foolproof check, but better than nothing + if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/jpeg'; + } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/png'; + } elseif (preg_match('#^GIF#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/gif'; + } + break; + + case 'atID': + case 'cnID': + case 'geID': + case 'tves': + case 'tvsn': + default: + // 32-bit integer + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + } + break; + + case 1: // text flag + case 13: // image flag + default: + $atom_structure['data'] = substr($boxdata, 8); + if ($atomname == 'covr') { // not a foolproof check, but better than nothing if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { $atom_structure['image_mime'] = 'image/jpeg'; @@ -513,1074 +554,1312 @@ class getid3_quicktime extends getid3_handler } elseif (preg_match('#^GIF#', $atom_structure['data'])) { $atom_structure['image_mime'] = 'image/gif'; } - break; - - case 'atID': - case 'cnID': - case 'geID': - case 'tves': - case 'tvsn': - default: - // 32-bit integer - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); - } - break; - - case 1: // text flag - case 13: // image flag - default: - $atom_structure['data'] = substr($boxdata, 8); - if ($atomname == 'covr') { - // not a foolproof check, but better than nothing - if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/jpeg'; - } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/png'; - } elseif (preg_match('#^GIF#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/gif'; } - } - break; + break; - } - break; + } + break; - default: - $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset); - $atom_structure['data'] = $atom_data; + default: + $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset); + $atom_structure['data'] = $atom_data; + } } } } - } - $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); - break; + $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); + break; - case 'play': // auto-PLAY atom - $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + case 'play': // auto-PLAY atom + $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $info['quicktime']['autoplay'] = $atom_structure['autoplay']; - break; + $info['quicktime']['autoplay'] = $atom_structure['autoplay']; + break; - case 'WLOC': // Window LOCation atom - $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); - $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); - break; + case 'WLOC': // Window LOCation atom + $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); + break; - case 'LOOP': // LOOPing atom - case 'SelO': // play SELection Only atom - case 'AllF': // play ALL Frames atom - $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); - break; + case 'LOOP': // LOOPing atom + case 'SelO': // play SELection Only atom + case 'AllF': // play ALL Frames atom + $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); + break; - case 'name': // - case 'MCPS': // Media Cleaner PRo - case '@PRM': // adobe PReMiere version - case '@PRQ': // adobe PRemiere Quicktime version - $atom_structure['data'] = $atom_data; - break; + case 'name': // + case 'MCPS': // Media Cleaner PRo + case '@PRM': // adobe PReMiere version + case '@PRQ': // adobe PRemiere Quicktime version + $atom_structure['data'] = $atom_data; + break; - case 'cmvd': // Compressed MooV Data atom - // Code by ubergeekØubergeek*tv based on information from - // http://developer.apple.com/quicktime/icefloe/dispatch012.html - $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + case 'cmvd': // Compressed MooV Data atom + // Code by ubergeekØubergeek*tv based on information from + // http://developer.apple.com/quicktime/icefloe/dispatch012.html + $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); - $CompressedFileData = substr($atom_data, 4); - if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms); - } else { - $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']); - } - break; + $CompressedFileData = substr($atom_data, 4); + if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms); + } else { + $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']); + } + break; - case 'dcom': // Data COMpression atom - $atom_structure['compression_id'] = $atom_data; - $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data); - break; + case 'dcom': // Data COMpression atom + $atom_structure['compression_id'] = $atom_data; + $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data); + break; - case 'rdrf': // Reference movie Data ReFerence atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001); + case 'rdrf': // Reference movie Data ReFerence atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001); - $atom_structure['reference_type_name'] = substr($atom_data, 4, 4); - $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - switch ($atom_structure['reference_type_name']) { - case 'url ': - $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12)); - break; + $atom_structure['reference_type_name'] = substr($atom_data, 4, 4); + $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + switch ($atom_structure['reference_type_name']) { + case 'url ': + $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12)); + break; - case 'alis': - $atom_structure['file_alias'] = substr($atom_data, 12); - break; + case 'alis': + $atom_structure['file_alias'] = substr($atom_data, 12); + break; - case 'rsrc': - $atom_structure['resource_alias'] = substr($atom_data, 12); - break; + case 'rsrc': + $atom_structure['resource_alias'] = substr($atom_data, 12); + break; - default: - $atom_structure['data'] = substr($atom_data, 12); - break; - } - break; + default: + $atom_structure['data'] = substr($atom_data, 12); + break; + } + break; - case 'rmqu': // Reference Movie QUality atom - $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data); - break; + case 'rmqu': // Reference Movie QUality atom + $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data); + break; - case 'rmcs': // Reference Movie Cpu Speed atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - break; + case 'rmcs': // Reference Movie Cpu Speed atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + break; - case 'rmvc': // Reference Movie Version Check atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4); - $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); - $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); - break; + case 'rmvc': // Reference Movie Version Check atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4); + $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); + break; - case 'rmcd': // Reference Movie Component check atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['component_type'] = substr($atom_data, 4, 4); - $atom_structure['component_subtype'] = substr($atom_data, 8, 4); - $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); - $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); - $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); - $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4)); - break; + case 'rmcd': // Reference Movie Component check atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['component_type'] = substr($atom_data, 4, 4); + $atom_structure['component_subtype'] = substr($atom_data, 8, 4); + $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); + $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4)); + break; - case 'rmdr': // Reference Movie Data Rate atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + case 'rmdr': // Reference Movie Data Rate atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10; - break; + $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10; + break; - case 'rmla': // Reference Movie Language Atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + case 'rmla': // Reference Movie Language Atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); - if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { - $info['comments']['language'][] = $atom_structure['language']; - } - break; + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; + } + break; - case 'rmla': // Reference Movie Language Atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - break; + case 'ptv ': // Print To Video - defines a movie's full screen mode + // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm + $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000 + $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000 + $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1)); + $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1)); + + $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag']; + $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag']; + + $ptv_lookup[0] = 'normal'; + $ptv_lookup[1] = 'double'; + $ptv_lookup[2] = 'half'; + $ptv_lookup[3] = 'full'; + $ptv_lookup[4] = 'current'; + if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { + $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; + } else { + $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'); + } + break; - case 'ptv ': // Print To Video - defines a movie's full screen mode - // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm - $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); - $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000 - $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000 - $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1)); - $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1)); - - $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag']; - $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag']; - - $ptv_lookup[0] = 'normal'; - $ptv_lookup[1] = 'double'; - $ptv_lookup[2] = 'half'; - $ptv_lookup[3] = 'full'; - $ptv_lookup[4] = 'current'; - if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { - $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; - } else { - $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'); - } - break; + case 'stsd': // Sample Table Sample Description atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + // see: https://github.com/JamesHeinrich/getID3/issues/111 + // Some corrupt files have been known to have high bits set in the number_entries field + // This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000 + // Workaround: mask off the upper byte and throw a warning if it's nonzero + if ($atom_structure['number_entries'] > 0x000FFFFF) { + if ($atom_structure['number_entries'] > 0x00FFFFFF) { + $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF)); + $atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF); + } else { + $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to info@getid3.org referencing bug report #111'); + } + } - case 'stsd': // Sample Table Sample Description atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $stsdEntriesDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); - $stsdEntriesDataOffset += 4; - $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); - $stsdEntriesDataOffset += 4; - $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6)); - $stsdEntriesDataOffset += 6; - $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2)); - $stsdEntriesDataOffset += 2; - $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); - $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); - - $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); - $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); - $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); - - switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { - - case "\x00\x00\x00\x00": - // audio tracks - $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2)); - $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2)); - $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2)); - $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2)); - $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); - - // video tracks - // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html - $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); - $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); - $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); - $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); - $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); - $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); - $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); - $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); - $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); - $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); - $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); - - switch ($atom_structure['sample_description_table'][$i]['data_format']) { - case '2vuY': - case 'avc1': - case 'cvid': - case 'dvc ': - case 'dvcp': - case 'gif ': - case 'h263': - case 'jpeg': - case 'kpcd': - case 'mjpa': - case 'mjpb': - case 'mp4v': - case 'png ': - case 'raw ': - case 'rle ': - case 'rpza': - case 'smc ': - case 'SVQ1': - case 'SVQ3': - case 'tiff': - case 'v210': - case 'v216': - case 'v308': - case 'v408': - case 'v410': - case 'yuv2': - $info['fileformat'] = 'mp4'; - $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; -// http://www.getid3.org/phpBB3/viewtopic.php?t=1550 -//if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers -if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) { - // assume that values stored here are more important than values stored in [tkhd] atom - $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width']; - $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height']; - $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; - $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; -} - break; + $stsdEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); + $stsdEntriesDataOffset += 4; + $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); + $stsdEntriesDataOffset += 4; + $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6)); + $stsdEntriesDataOffset += 6; + $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2)); + $stsdEntriesDataOffset += 2; + $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); + $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); + + $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); + $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); + $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); + + switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { + + case "\x00\x00\x00\x00": + // audio tracks + $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2)); + $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2)); + $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2)); + $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2)); + $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); + + // video tracks + // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html + $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); + $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); + $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); + $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); + $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); + $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); + $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); + $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); + $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); + $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); + $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); + + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case '2vuY': + case 'avc1': + case 'cvid': + case 'dvc ': + case 'dvcp': + case 'gif ': + case 'h263': + case 'jpeg': + case 'kpcd': + case 'mjpa': + case 'mjpb': + case 'mp4v': + case 'png ': + case 'raw ': + case 'rle ': + case 'rpza': + case 'smc ': + case 'SVQ1': + case 'SVQ3': + case 'tiff': + case 'v210': + case 'v216': + case 'v308': + case 'v408': + case 'v410': + case 'yuv2': + $info['fileformat'] = 'mp4'; + $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; + if ($this->QuicktimeVideoCodecLookup($info['video']['fourcc'])) { + $info['video']['fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']); + } + + // https://www.getid3.org/phpBB3/viewtopic.php?t=1550 + //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers + if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) { + // assume that values stored here are more important than values stored in [tkhd] atom + $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width']; + $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height']; + $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; + $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; + } + break; - case 'qtvr': - $info['video']['dataformat'] = 'quicktimevr'; - break; + case 'qtvr': + $info['video']['dataformat'] = 'quicktimevr'; + break; - case 'mp4a': - default: - $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); - $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; - $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; - $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; - $info['audio']['codec'] = $info['quicktime']['audio']['codec']; - $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate']; - $info['audio']['channels'] = $info['quicktime']['audio']['channels']; - $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth']; - switch ($atom_structure['sample_description_table'][$i]['data_format']) { - case 'raw ': // PCM - case 'alac': // Apple Lossless Audio Codec - $info['audio']['lossless'] = true; - break; - default: - $info['audio']['lossless'] = false; - break; - } - break; - } - break; + case 'mp4a': + default: + $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; + $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; + $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; + $info['audio']['codec'] = $info['quicktime']['audio']['codec']; + $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate']; + $info['audio']['channels'] = $info['quicktime']['audio']['channels']; + $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth']; + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case 'raw ': // PCM + case 'alac': // Apple Lossless Audio Codec + case 'sowt': // signed/two's complement (Little Endian) + case 'twos': // signed/two's complement (Big Endian) + case 'in24': // 24-bit Integer + case 'in32': // 32-bit Integer + case 'fl32': // 32-bit Floating Point + case 'fl64': // 64-bit Floating Point + $info['audio']['lossless'] = $info['quicktime']['audio']['lossless'] = true; + $info['audio']['bitrate'] = $info['quicktime']['audio']['bitrate'] = $info['audio']['channels'] * $info['audio']['bits_per_sample'] * $info['audio']['sample_rate']; + break; + default: + $info['audio']['lossless'] = false; + break; + } + break; + } + break; - default: - switch ($atom_structure['sample_description_table'][$i]['data_format']) { - case 'mp4s': - $info['fileformat'] = 'mp4'; - break; + default: + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case 'mp4s': + $info['fileformat'] = 'mp4'; + break; - default: - // video atom - $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); - $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); - $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); - $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); - $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); - $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); - $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); - $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2)); - $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1)); - $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']); - $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); - $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); - - $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); - $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); - - if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { - $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; - $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); - $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']); - $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; - $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; - - $info['video']['codec'] = $info['quicktime']['video']['codec']; - $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth']; - } - $info['video']['lossless'] = false; - $info['video']['pixel_aspect_ratio'] = (float) 1; - break; - } - break; - } - switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { - case 'mp4a': - $info['audio']['dataformat'] = 'mp4'; - $info['quicktime']['audio']['codec'] = 'mp4'; - break; + default: + // video atom + $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); + $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); + $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); + $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); + $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); + $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); + $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); + $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2)); + $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1)); + $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']); + $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); + $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); + + $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); + $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); + + if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { + $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; + $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; + $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; + + $info['video']['codec'] = $info['quicktime']['video']['codec']; + $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth']; + } + $info['video']['lossless'] = false; + $info['video']['pixel_aspect_ratio'] = (float) 1; + break; + } + break; + } + switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { + case 'mp4a': + $info['audio']['dataformat'] = 'mp4'; + $info['quicktime']['audio']['codec'] = 'mp4'; + break; - case '3ivx': - case '3iv1': - case '3iv2': - $info['video']['dataformat'] = '3ivx'; - break; + case '3ivx': + case '3iv1': + case '3iv2': + $info['video']['dataformat'] = '3ivx'; + break; - case 'xvid': - $info['video']['dataformat'] = 'xvid'; - break; + case 'xvid': + $info['video']['dataformat'] = 'xvid'; + break; - case 'mp4v': - $info['video']['dataformat'] = 'mpeg4'; - break; + case 'mp4v': + $info['video']['dataformat'] = 'mpeg4'; + break; - case 'divx': - case 'div1': - case 'div2': - case 'div3': - case 'div4': - case 'div5': - case 'div6': - $info['video']['dataformat'] = 'divx'; - break; + case 'divx': + case 'div1': + case 'div2': + case 'div3': + case 'div4': + case 'div5': + case 'div6': + $info['video']['dataformat'] = 'divx'; + break; - default: - // do nothing - break; + default: + // do nothing + break; + } + unset($atom_structure['sample_description_table'][$i]['data']); } - unset($atom_structure['sample_description_table'][$i]['data']); - } - break; + break; - case 'stts': // Sample Table Time-to-Sample atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $sttsEntriesDataOffset = 8; - //$FrameRateCalculatorArray = array(); - $frames_count = 0; + case 'stts': // Sample Table Time-to-Sample atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $sttsEntriesDataOffset = 8; + //$FrameRateCalculatorArray = array(); + $frames_count = 0; - $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']); - if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { - $this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($atom_structure['number_entries'] / 1048576).'MB).'); - } - for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { - $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); - $sttsEntriesDataOffset += 4; - $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); - $sttsEntriesDataOffset += 4; - - $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; - - // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM - //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { - // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; - // if ($stts_new_framerate <= 60) { - // // some atoms have durations of "1" giving a very large framerate, which probably is not right - // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); + $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']); + if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { + $this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).'); + } + for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { + $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + + $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; + + // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM + //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { + // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; + // if ($stts_new_framerate <= 60) { + // // some atoms have durations of "1" giving a very large framerate, which probably is not right + // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); + // } + //} + // + //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; + } + $info['quicktime']['stts_framecount'][] = $frames_count; + //$sttsFramesTotal = 0; + //$sttsSecondsTotal = 0; + //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { + // if (($frames_per_second > 60) || ($frames_per_second < 1)) { + // // not video FPS information, probably audio information + // $sttsFramesTotal = 0; + // $sttsSecondsTotal = 0; + // break; // } + // $sttsFramesTotal += $frame_count; + // $sttsSecondsTotal += $frame_count / $frames_per_second; //} - // - //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; - } - $info['quicktime']['stts_framecount'][] = $frames_count; - //$sttsFramesTotal = 0; - //$sttsSecondsTotal = 0; - //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { - // if (($frames_per_second > 60) || ($frames_per_second < 1)) { - // // not video FPS information, probably audio information - // $sttsFramesTotal = 0; - // $sttsSecondsTotal = 0; - // break; - // } - // $sttsFramesTotal += $frame_count; - // $sttsSecondsTotal += $frame_count / $frames_per_second; - //} - //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { - // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { - // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; - // } - //} - break; + //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { + // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { + // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; + // } + //} + break; - case 'stss': // Sample Table Sync Sample (key frames) atom - if ($ParseAllPossibleAtoms) { - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $stssEntriesDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4)); - $stssEntriesDataOffset += 4; + case 'stss': // Sample Table Sync Sample (key frames) atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stssEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4)); + $stssEntriesDataOffset += 4; + } } - } - break; + break; - case 'stsc': // Sample Table Sample-to-Chunk atom - if ($ParseAllPossibleAtoms) { - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $stscEntriesDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; - $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; - $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; + case 'stsc': // Sample Table Sample-to-Chunk atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stscEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + } } - } - break; + break; - case 'stsz': // Sample Table SiZe atom - if ($ParseAllPossibleAtoms) { - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $stszEntriesDataOffset = 12; - if ($atom_structure['sample_size'] == 0) { + case 'stsz': // Sample Table SiZe atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $stszEntriesDataOffset = 12; + if ($atom_structure['sample_size'] == 0) { + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4)); + $stszEntriesDataOffset += 4; + } + } + } + break; + + + case 'stco': // Sample Table Chunk Offset atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stcoEntriesDataOffset = 8; for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4)); - $stszEntriesDataOffset += 4; + $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4)); + $stcoEntriesDataOffset += 4; } } - } - break; + break; - case 'stco': // Sample Table Chunk Offset atom - if ($ParseAllPossibleAtoms) { - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $stcoEntriesDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4)); - $stcoEntriesDataOffset += 4; + case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files) + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stcoEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8)); + $stcoEntriesDataOffset += 8; + } } - } - break; + break; - case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files) - if ($ParseAllPossibleAtoms) { + case 'dref': // Data REFerence atom $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $stcoEntriesDataOffset = 8; + $drefDataOffset = 8; for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8)); - $stcoEntriesDataOffset += 8; + $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); + $drefDataOffset += 4; + $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); + $drefDataOffset += 4; + $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); + $drefDataOffset += 1; + $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 + $drefDataOffset += 3; + $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); + $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); + + $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); } - } - break; + break; - case 'dref': // Data REFerence atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $drefDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { - $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); - $drefDataOffset += 4; - $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); - $drefDataOffset += 4; - $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); - $drefDataOffset += 1; - $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 - $drefDataOffset += 3; - $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); - $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); - - $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); - } - break; + case 'gmin': // base Media INformation atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); + $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); + $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2)); + $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); + break; - case 'gmin': // base Media INformation atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); - $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); - $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); - $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2)); - $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); - break; + case 'smhd': // Sound Media information HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + break; - case 'smhd': // Sound Media information HeaDer atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); - break; + case 'vmhd': // Video Media information HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); + $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); + $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001); + break; - case 'vmhd': // Video Media information HeaDer atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); - $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); - $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); - $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); - $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001); - break; + case 'hdlr': // HanDLeR reference atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['component_type'] = substr($atom_data, 4, 4); + $atom_structure['component_subtype'] = substr($atom_data, 8, 4); + $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); + $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); + if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { + $info['video']['dataformat'] = 'quicktimevr'; + } + break; - case 'hdlr': // HanDLeR reference atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['component_type'] = substr($atom_data, 4, 4); - $atom_structure['component_subtype'] = substr($atom_data, 8, 4); - $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); - $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); - $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); - $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); - if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { - $info['video']['dataformat'] = 'quicktimevr'; - } - break; + case 'mdhd': // MeDia HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2)); + $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); + + if ($atom_structure['time_scale'] == 0) { + $this->error('Corrupt Quicktime file: mdhd.time_scale == zero'); + return false; + } + $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; + } + break; - case 'mdhd': // MeDia HeaDer atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); - $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); - $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2)); - $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); - if ($atom_structure['time_scale'] == 0) { - $this->error('Corrupt Quicktime file: mdhd.time_scale == zero'); - return false; - } - $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); - - $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); - $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); - $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; - $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); - if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { - $info['comments']['language'][] = $atom_structure['language']; - } - break; + case 'pnot': // Preview atom + $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" + $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 + $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' + $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 + $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); + break; - case 'pnot': // Preview atom - $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" - $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 - $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' - $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 - $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); - break; + case 'crgn': // Clipping ReGioN atom + $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, + $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields + $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. + break; - case 'crgn': // Clipping ReGioN atom - $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, - $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields - $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. - break; + case 'load': // track LOAD settings atom + $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020); + $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100); + break; - case 'load': // track LOAD settings atom - $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); - $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); - $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020); - $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100); - break; + case 'tmcd': // TiMe CoDe atom + case 'chap': // CHAPter list atom + case 'sync': // SYNChronization atom + case 'scpt': // tranSCriPT atom + case 'ssrc': // non-primary SouRCe atom + for ($i = 0; $i < strlen($atom_data); $i += 4) { + @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); + } + break; - case 'tmcd': // TiMe CoDe atom - case 'chap': // CHAPter list atom - case 'sync': // SYNChronization atom - case 'scpt': // tranSCriPT atom - case 'ssrc': // non-primary SouRCe atom - for ($i = 0; $i < strlen($atom_data); $i += 4) { - @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); - } - break; + case 'elst': // Edit LiST atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) { + $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); + $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); + $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); + } + break; - case 'elst': // Edit LiST atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) { - $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); - $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); - $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); - } - break; + case 'kmat': // compressed MATte atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['matte_data_raw'] = substr($atom_data, 4); + break; - case 'kmat': // compressed MATte atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 - $atom_structure['matte_data_raw'] = substr($atom_data, 4); - break; + case 'ctab': // Color TABle atom + $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 + $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 + $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; + for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { + $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); + $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); + $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); + $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); + } + break; - case 'ctab': // Color TABle atom - $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 - $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 - $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; - for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { - $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); - $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); - $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); - $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); - } - break; + case 'mvhd': // MoVie HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4)); + $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2)); + $atom_structure['reserved'] = substr($atom_data, 26, 10); + $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4)); + $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); + $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4)); + $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4)); + $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); + $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4)); + $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4)); + $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); + $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4)); + $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4)); + $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4)); + $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4)); + $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4)); + $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4)); + $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4)); + $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); + + if ($atom_structure['time_scale'] == 0) { + $this->error('Corrupt Quicktime file: mvhd.time_scale == zero'); + return false; + } + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; + $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; + break; - case 'mvhd': // MoVie HeaDer atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); - $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); - $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4)); - $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2)); - $atom_structure['reserved'] = substr($atom_data, 26, 10); - $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4)); - $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); - $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4)); - $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4)); - $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); - $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4)); - $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4)); - $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); - $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4)); - $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4)); - $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4)); - $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4)); - $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4)); - $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4)); - $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4)); - $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); - - if ($atom_structure['time_scale'] == 0) { - $this->error('Corrupt Quicktime file: mvhd.time_scale == zero'); - return false; - } - $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); - $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); - $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); - $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; - $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; - break; + case 'tkhd': // TracK HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8)); + $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2)); + $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2)); + $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2)); + $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); + // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html + // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737 + $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); + $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4)); + $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4)); + $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); + $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4)); + $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4)); + $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); + $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4)); + $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4)); + $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4)); + $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4)); + $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001); + $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002); + $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004); + $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + + // https://www.getid3.org/phpBB3/viewtopic.php?t=1908 + // attempt to compute rotation from matrix values + // 2017-Dec-28: uncertain if 90/270 are correctly oriented; values returned by FixedPoint16_16 should perhaps be -1 instead of 65535(?) + $matrixRotation = 0; + switch ($atom_structure['matrix_a'].':'.$atom_structure['matrix_b'].':'.$atom_structure['matrix_c'].':'.$atom_structure['matrix_d']) { + case '1:0:0:1': $matrixRotation = 0; break; + case '0:1:65535:0': $matrixRotation = 90; break; + case '65535:0:0:65535': $matrixRotation = 180; break; + case '0:65535:1:0': $matrixRotation = 270; break; + default: break; + } + // https://www.getid3.org/phpBB3/viewtopic.php?t=2468 + // The rotation matrix can appear in the Quicktime file multiple times, at least once for each track, + // and it's possible that only the video track (or, in theory, one of the video tracks) is flagged as + // rotated while the other tracks (e.g. audio) is tagged as rotation=0 (behavior noted on iPhone 8 Plus) + // The correct solution would be to check if the TrackID associated with the rotation matrix is indeed + // a video track (or the main video track) and only set the rotation then, but since information about + // what track is what is not trivially there to be examined, the lazy solution is to set the rotation + // if it is found to be nonzero, on the assumption that tracks that don't need it will have rotation set + // to zero (and be effectively ignored) and the video track will have rotation set correctly, which will + // either be zero and automatically correct, or nonzero and be set correctly. + if (!isset($info['video']['rotate']) || (($info['video']['rotate'] == 0) && ($matrixRotation > 0))) { + $info['quicktime']['video']['rotate'] = $info['video']['rotate'] = $matrixRotation; + } - case 'tkhd': // TracK HeaDer atom - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); - $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); - $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); - $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); - $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8)); - $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2)); - $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2)); - $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2)); - $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); -// http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html -// http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737 - $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); - $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4)); - $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4)); - $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); - $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4)); - $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4)); - $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); - $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4)); - $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4)); - $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4)); - $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4)); - $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001); - $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002); - $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004); - $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); - $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); - $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); - - if ($atom_structure['flags']['enabled'] == 1) { - if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { - $info['video']['resolution_x'] = $atom_structure['width']; - $info['video']['resolution_y'] = $atom_structure['height']; + if ($atom_structure['flags']['enabled'] == 1) { + if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { + $info['video']['resolution_x'] = $atom_structure['width']; + $info['video']['resolution_y'] = $atom_structure['height']; + } + $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); + $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); + $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; + $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; + } else { + // see: https://www.getid3.org/phpBB3/viewtopic.php?t=1295 + //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); } + //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); } + //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); } } - $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); - $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); - $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; - $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; - } else { - // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295 - //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); } - //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); } - //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); } - } - break; + break; - case 'iods': // Initial Object DeScriptor atom - // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h - // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html - $offset = 0; - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3)); - $offset += 3; - $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); - //$offset already adjusted by quicktime_read_mp4_descr_length() - $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); - $offset += 2; - $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); - $offset += 1; - - $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields - for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) { - $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + case 'iods': // Initial Object DeScriptor atom + // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h + // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html + $offset = 0; + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); $offset += 1; - $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3)); + $offset += 3; + $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); //$offset already adjusted by quicktime_read_mp4_descr_length() - $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); - $offset += 4; - } + $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); + $offset += 2; + $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; - $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']); - $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); - break; + $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields + for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) { + $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); + //$offset already adjusted by quicktime_read_mp4_descr_length() + $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); + $offset += 4; + } - case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) - $atom_structure['signature'] = substr($atom_data, 0, 4); - $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $atom_structure['fourcc'] = substr($atom_data, 8, 4); - break; + $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']); + $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); + break; - case 'mdat': // Media DATa atom - // 'mdat' contains the actual data for the audio/video, possibly also subtitles + case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) + $atom_structure['signature'] = substr($atom_data, 0, 4); + $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['fourcc'] = substr($atom_data, 8, 4); + break; -/* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */ + case 'mdat': // Media DATa atom + // 'mdat' contains the actual data for the audio/video, possibly also subtitles - // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?) - $mdat_offset = 0; - while (true) { - if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') { - $mdat_offset += 8; - } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') { - $mdat_offset += 8; - } else { - break; - } - } + /* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */ - // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field - while (($mdat_offset < (strlen($atom_data) - 8)) - && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) - && ($chapter_string_length < 1000) - && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) - && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) { - list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches; - $mdat_offset += (2 + $chapter_string_length); - @$info['quicktime']['comments']['chapters'][] = $chapter_string; - - // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled) - if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8 - $mdat_offset += 12; + // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?) + $mdat_offset = 0; + while (true) { + if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') { + $mdat_offset += 8; + } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') { + $mdat_offset += 8; + } else { + break; } - } - - - if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { + } + if (substr($atom_data, $mdat_offset, 4) == 'GPRO') { + $GOPRO_chunk_length = getid3_lib::LittleEndian2Int(substr($atom_data, $mdat_offset + 4, 4)); + $GOPRO_offset = 8; + $atom_structure['GPRO']['raw'] = substr($atom_data, $mdat_offset + 8, $GOPRO_chunk_length - 8); + $atom_structure['GPRO']['firmware'] = substr($atom_structure['GPRO']['raw'], 0, 15); + $atom_structure['GPRO']['unknown1'] = substr($atom_structure['GPRO']['raw'], 15, 16); + $atom_structure['GPRO']['unknown2'] = substr($atom_structure['GPRO']['raw'], 31, 32); + $atom_structure['GPRO']['unknown3'] = substr($atom_structure['GPRO']['raw'], 63, 16); + $atom_structure['GPRO']['camera'] = substr($atom_structure['GPRO']['raw'], 79, 32); + $info['quicktime']['camera']['model'] = rtrim($atom_structure['GPRO']['camera'], "\x00"); + } - $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8; - $OldAVDataEnd = $info['avdataend']; - $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; + // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field + while (($mdat_offset < (strlen($atom_data) - 8)) + && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) + && ($chapter_string_length < 1000) + && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) + && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) { + list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches; + $mdat_offset += (2 + $chapter_string_length); + @$info['quicktime']['comments']['chapters'][] = $chapter_string; + + // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled) + if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8 + $mdat_offset += 12; + } + } - $getid3_temp = new getID3(); - $getid3_temp->openfile($this->getid3->filename); - $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; - $getid3_temp->info['avdataend'] = $info['avdataend']; - $getid3_mp3 = new getid3_mp3($getid3_temp); - if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) { - $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); - if (!empty($getid3_temp->info['warning'])) { - foreach ($getid3_temp->info['warning'] as $value) { - $this->warning($value); + if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { + + $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8; + $OldAVDataEnd = $info['avdataend']; + $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_mp3 = new getid3_mp3($getid3_temp); + if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) { + $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $value) { + $this->warning($value); + } } - } - if (!empty($getid3_temp->info['mpeg'])) { - $info['mpeg'] = $getid3_temp->info['mpeg']; - if (isset($info['mpeg']['audio'])) { - $info['audio']['dataformat'] = 'mp3'; - $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); - $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; - $info['audio']['channels'] = $info['mpeg']['audio']['channels']; - $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; - $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); - $info['bitrate'] = $info['audio']['bitrate']; + if (!empty($getid3_temp->info['mpeg'])) { + $info['mpeg'] = $getid3_temp->info['mpeg']; + if (isset($info['mpeg']['audio'])) { + $info['audio']['dataformat'] = 'mp3'; + $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + $info['bitrate'] = $info['audio']['bitrate']; + } } } - } - unset($getid3_mp3, $getid3_temp); - $info['avdataend'] = $OldAVDataEnd; - unset($OldAVDataEnd); + unset($getid3_mp3, $getid3_temp); + $info['avdataend'] = $OldAVDataEnd; + unset($OldAVDataEnd); - } + } - unset($mdat_offset, $chapter_string_length, $chapter_matches); - break; + unset($mdat_offset, $chapter_string_length, $chapter_matches); + break; - case 'free': // FREE space atom - case 'skip': // SKIP atom - case 'wide': // 64-bit expansion placeholder atom - // 'free', 'skip' and 'wide' are just padding, contains no useful data at all - - // When writing QuickTime files, it is sometimes necessary to update an atom's size. - // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom - // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime - // puts an 8-byte placeholder atom before any atoms it may have to update the size of. - // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the - // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. - // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). - break; + case 'free': // FREE space atom + case 'skip': // SKIP atom + case 'wide': // 64-bit expansion placeholder atom + // 'free', 'skip' and 'wide' are just padding, contains no useful data at all + + // When writing QuickTime files, it is sometimes necessary to update an atom's size. + // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom + // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime + // puts an 8-byte placeholder atom before any atoms it may have to update the size of. + // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the + // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. + // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). + break; - case 'nsav': // NoSAVe atom - // http://developer.apple.com/technotes/tn/tn2038.html - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); - break; + case 'nsav': // NoSAVe atom + // http://developer.apple.com/technotes/tn/tn2038.html + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + break; - case 'ctyp': // Controller TYPe atom (seen on QTVR) - // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt - // some controller names are: - // 0x00 + 'std' for linear movie - // 'none' for no controls - $atom_structure['ctyp'] = substr($atom_data, 0, 4); - $info['quicktime']['controller'] = $atom_structure['ctyp']; - switch ($atom_structure['ctyp']) { - case 'qtvr': - $info['video']['dataformat'] = 'quicktimevr'; - break; - } - break; + case 'ctyp': // Controller TYPe atom (seen on QTVR) + // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt + // some controller names are: + // 0x00 + 'std' for linear movie + // 'none' for no controls + $atom_structure['ctyp'] = substr($atom_data, 0, 4); + $info['quicktime']['controller'] = $atom_structure['ctyp']; + switch ($atom_structure['ctyp']) { + case 'qtvr': + $info['video']['dataformat'] = 'quicktimevr'; + break; + } + break; - case 'pano': // PANOrama track (seen on QTVR) - $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); - break; + case 'pano': // PANOrama track (seen on QTVR) + $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + break; - case 'hint': // HINT track - case 'hinf': // - case 'hinv': // - case 'hnti': // - $info['quicktime']['hinting'] = true; - break; + case 'hint': // HINT track + case 'hinf': // + case 'hinv': // + case 'hnti': // + $info['quicktime']['hinting'] = true; + break; - case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) - for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { - $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); - } - break; + case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) + for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { + $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); + } + break; - // Observed-but-not-handled atom types are just listed here to prevent warnings being generated - case 'FXTC': // Something to do with Adobe After Effects (?) - case 'PrmA': - case 'code': - case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html - case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html - // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838] - // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html - // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html - case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html - case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html - case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html - case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html - //$atom_structure['data'] = $atom_data; - break; + // Observed-but-not-handled atom types are just listed here to prevent warnings being generated + case 'FXTC': // Something to do with Adobe After Effects (?) + case 'PrmA': + case 'code': + case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html + case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html + // tapt seems to be used to compute the video size [https://www.getid3.org/phpBB3/viewtopic.php?t=838] + // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html + // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html + case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + //$atom_structure['data'] = $atom_data; + break; - case "\xA9".'xyz': // GPS latitude+longitude+altitude - $atom_structure['data'] = $atom_data; - if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { - @list($all, $latitude, $longitude, $altitude) = $matches; - $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); - $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); - if (!empty($altitude)) { - $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); + case "\xA9".'xyz': // GPS latitude+longitude+altitude + $atom_structure['data'] = $atom_data; + if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { + @list($all, $latitude, $longitude, $altitude) = $matches; + $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); + $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); + if (!empty($altitude)) { + $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); + } + } else { + $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'); } - } else { - $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'); - } - break; + break; - case 'NCDT': - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); - break; - case 'NCTH': // Nikon Camera THumbnail image - case 'NCVW': // Nikon Camera preVieW image - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { + case 'NCDT': + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); + break; + case 'NCTH': // Nikon Camera THumbnail image + case 'NCVW': // Nikon Camera preVieW image + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { + $atom_structure['data'] = $atom_data; + $atom_structure['image_mime'] = 'image/jpeg'; + $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); + $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); + } + break; + case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG + $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); + break; + case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html $atom_structure['data'] = $atom_data; - $atom_structure['image_mime'] = 'image/jpeg'; - $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); - $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); - } - break; - case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG - $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); - break; - case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html - $atom_structure['data'] = $atom_data; - break; + break; - case "\x00\x00\x00\x00": - // some kind of metacontainer, may contain a big data dump such as: - // mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4 - // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt + case "\x00\x00\x00\x00": + // some kind of metacontainer, may contain a big data dump such as: + // mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4 + // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; - case 'meta': // METAdata atom - // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html + case 'meta': // METAdata atom + // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; - case 'data': // metaDATA atom - static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other - // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data - $atom_structure['language'] = substr($atom_data, 4 + 0, 2); - $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); - $atom_structure['data'] = substr($atom_data, 4 + 4); - $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; + case 'data': // metaDATA atom + static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other + // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data + $atom_structure['language'] = substr($atom_data, 4 + 0, 2); + $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); + $atom_structure['data'] = substr($atom_data, 4 + 4); + $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; - if ($atom_structure['key_name'] && $atom_structure['data']) { - @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; - } - break; + if ($atom_structure['key_name'] && $atom_structure['data']) { + @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; + } + break; - case 'keys': // KEYS that may be present in the metadata atom. - // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21 - // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom. - // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys". - $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); - $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); - $keys_atom_offset = 8; - for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { - $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); - $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); - $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); - $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace - - $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; - } - break; + case 'keys': // KEYS that may be present in the metadata atom. + // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21 + // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom. + // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys". + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $keys_atom_offset = 8; + for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { + $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); + $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); + $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); + $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace + + $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; + } + break; - default: - $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset); - $atom_structure['data'] = $atom_data; - break; + case 'gps ': + // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 + // The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data. + // The first row is version/metadata/notsure, I skip that. + // The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file. + + $GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size + if (strlen($atom_data) > 0) { + if ((strlen($atom_data) % $GPS_rowsize) == 0) { + $atom_structure['gps_toc'] = array(); + foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) { + $atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize)); + } + + $atom_structure['gps_entries'] = array(); + $previous_offset = $this->ftell(); + foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) { + if ($key == 0) { + // "The first row is version/metadata/notsure, I skip that." + continue; + } + $this->fseek($gps_pointer['offset']); + $GPS_free_data = $this->fread($gps_pointer['size']); + + /* + // 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead + + // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 + // The structure of the GPS data atom (the 'free' atoms mentioned above) is following: + // hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48) + // For those unfamiliar with python struct: + // I = int + // s = is string (size 1, in this case) + // f = float + + //$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48)); + */ + + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + // $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67 + // $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F + // $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D + if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) { + $GPS_this_GPRMC = array(); + list( + $GPS_this_GPRMC['raw']['gprmc'], + $GPS_this_GPRMC['raw']['timestamp'], + $GPS_this_GPRMC['raw']['status'], + $GPS_this_GPRMC['raw']['latitude'], + $GPS_this_GPRMC['raw']['latitude_direction'], + $GPS_this_GPRMC['raw']['longitude'], + $GPS_this_GPRMC['raw']['longitude_direction'], + $GPS_this_GPRMC['raw']['knots'], + $GPS_this_GPRMC['raw']['angle'], + $GPS_this_GPRMC['raw']['datestamp'], + $GPS_this_GPRMC['raw']['variation'], + $GPS_this_GPRMC['raw']['variation_direction'], + $dummy, + $GPS_this_GPRMC['raw']['checksum'], + ) = $matches; + + $hour = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2); + $minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2); + $second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2); + $ms = substr($GPS_this_GPRMC['raw']['timestamp'], 6); // may contain decimal seconds + $day = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2); + $month = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2); + $year = substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2); + $year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess + $GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms; + + $GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void + + foreach (array('latitude','longitude') as $latlon) { + preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches); + list($dummy, $deg, $min) = $matches; + $GPS_this_GPRMC[$latlon] = $deg + ($min / 60); + } + $GPS_this_GPRMC['latitude'] *= (($GPS_this_GPRMC['raw']['latitude_direction'] == 'S') ? -1 : 1); + $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1); + + $GPS_this_GPRMC['heading'] = $GPS_this_GPRMC['raw']['angle']; + $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots']; + $GPS_this_GPRMC['speed_kmh'] = $GPS_this_GPRMC['raw']['knots'] * 1.852; + if ($GPS_this_GPRMC['raw']['variation']) { + $GPS_this_GPRMC['variation'] = $GPS_this_GPRMC['raw']['variation']; + $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1); + } + + $atom_structure['gps_entries'][$key] = $GPS_this_GPRMC; + + @$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array( + 'latitude' => (float) $GPS_this_GPRMC['latitude'], + 'longitude' => (float) $GPS_this_GPRMC['longitude'], + 'speed_kmh' => (float) $GPS_this_GPRMC['speed_kmh'], + 'heading' => (float) $GPS_this_GPRMC['heading'], + ); + + } else { + $this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']); + } + } + $this->fseek($previous_offset); + + } else { + $this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset); + } + } else { + $this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset); + } + break; + + case 'loci':// 3GP location (El Loco) + $loffset = 0; + $info['quicktime']['comments']['gps_flags'] = array( getid3_lib::BigEndian2Int(substr($atom_data, 0, 4))); + $info['quicktime']['comments']['gps_lang'] = array( getid3_lib::BigEndian2Int(substr($atom_data, 4, 2))); + $info['quicktime']['comments']['gps_location'] = array( $this->LociString(substr($atom_data, 6), $loffset)); + $loci_data = substr($atom_data, 6 + $loffset); + $info['quicktime']['comments']['gps_role'] = array( getid3_lib::BigEndian2Int(substr($loci_data, 0, 1))); + $info['quicktime']['comments']['gps_longitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4))); + $info['quicktime']['comments']['gps_latitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4))); + $info['quicktime']['comments']['gps_altitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4))); + $info['quicktime']['comments']['gps_body'] = array( $this->LociString(substr($loci_data, 13 ), $loffset)); + $info['quicktime']['comments']['gps_notes'] = array( $this->LociString(substr($loci_data, 13 + $loffset), $loffset)); + break; + + case 'chpl': // CHaPter List + // https://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf + $chpl_version = getid3_lib::BigEndian2Int(substr($atom_data, 4, 1)); // Expected to be 0 + $chpl_flags = getid3_lib::BigEndian2Int(substr($atom_data, 5, 3)); // Reserved, set to 0 + $chpl_count = getid3_lib::BigEndian2Int(substr($atom_data, 8, 1)); + $chpl_offset = 9; + for ($i = 0; $i < $chpl_count; $i++) { + if (($chpl_offset + 9) >= strlen($atom_data)) { + $this->warning('QuickTime chapter '.$i.' extends beyond end of "chpl" atom'); + break; + } + $info['quicktime']['chapters'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 8)) / 10000000; // timestamps are stored as 100-nanosecond units + $chpl_offset += 8; + $chpl_title_size = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 1)); + $chpl_offset += 1; + $info['quicktime']['chapters'][$i]['title'] = substr($atom_data, $chpl_offset, $chpl_title_size); + $chpl_offset += $chpl_title_size; + } + break; + + case 'FIRM': // FIRMware version(?), seen on GoPro Hero4 + $info['quicktime']['camera']['firmware'] = $atom_data; + break; + + case 'CAME': // FIRMware version(?), seen on GoPro Hero4 + $info['quicktime']['camera']['serial_hash'] = unpack('H*', $atom_data); + break; + + case 'dscp': + case 'rcif': + // https://www.getid3.org/phpBB3/viewtopic.php?t=1908 + if (substr($atom_data, 0, 7) == "\x00\x00\x00\x00\x55\xC4".'{') { + if ($json_decoded = @json_decode(rtrim(substr($atom_data, 6), "\x00"), true)) { + $info['quicktime']['camera'][$atomname] = $json_decoded; + if (($atomname == 'rcif') && isset($info['quicktime']['camera']['rcif']['wxcamera']['rotate'])) { + $info['video']['rotate'] = $info['quicktime']['video']['rotate'] = $info['quicktime']['camera']['rcif']['wxcamera']['rotate']; + } + } else { + $this->warning('Failed to JSON decode atom "'.$atomname.'"'); + $atom_structure['data'] = $atom_data; + } + unset($json_decoded); + } else { + $this->warning('Expecting 55 C4 7B at start of atom "'.$atomname.'", found '.getid3_lib::PrintHexBytes(substr($atom_data, 4, 3)).' instead'); + $atom_structure['data'] = $atom_data; + } + break; + + case 'frea': + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea + // may contain "scra" (PreviewImage) and/or "thma" (ThumbnailImage) + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); + break; + case 'tima': // subatom to "frea" + // no idea what this does, the one sample file I've seen has a value of 0x00000027 + $atom_structure['data'] = $atom_data; + break; + case 'ver ': // subatom to "frea" + // some kind of version number, the one sample file I've seen has a value of "3.00.073" + $atom_structure['data'] = $atom_data; + break; + case 'thma': // subatom to "frea" -- "ThumbnailImage" + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea + if (strlen($atom_data) > 0) { + $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg'); + } + break; + case 'scra': // subatom to "frea" -- "PreviewImage" + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea + // but the only sample file I've seen has no useful data here + if (strlen($atom_data) > 0) { + $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg'); + } + break; + + + default: + $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset); + $atom_structure['data'] = $atom_data; + break; + } } array_pop($atomHierarchy); return $atom_structure; } + /** + * @param string $atom_data + * @param int $baseoffset + * @param array $atomHierarchy + * @param bool $ParseAllPossibleAtoms + * + * @return array|false + */ public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { -//echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>'; $atom_structure = false; $subatomoffset = 0; $subatomcounter = 0; @@ -1601,16 +1880,18 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ } return $atom_structure; } - - $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); - + $atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); $subatomoffset += $subatomsize; - $subatomcounter++; } return $atom_structure; } - + /** + * @param string $data + * @param int $offset + * + * @return int + */ public function quicktime_read_mp4_descr_length($data, &$offset) { // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html $num_bytes = 0; @@ -1622,7 +1903,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return $length; } - + /** + * @param int $languageid + * + * @return string + */ public function QuicktimeLanguageLookup($languageid) { // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353 static $QuicktimeLanguageLookup = array(); @@ -1760,6 +2045,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid'); } + /** + * @param string $codecid + * + * @return string + */ public function QuicktimeVideoCodecLookup($codecid) { static $QuicktimeVideoCodecLookup = array(); if (empty($QuicktimeVideoCodecLookup)) { @@ -1819,6 +2109,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : ''); } + /** + * @param string $codecid + * + * @return mixed|string + */ public function QuicktimeAudioCodecLookup($codecid) { static $QuicktimeAudioCodecLookup = array(); if (empty($QuicktimeAudioCodecLookup)) { @@ -1864,6 +2159,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : ''); } + /** + * @param string $compressionid + * + * @return string + */ public function QuicktimeDCOMLookup($compressionid) { static $QuicktimeDCOMLookup = array(); if (empty($QuicktimeDCOMLookup)) { @@ -1873,6 +2173,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : ''); } + /** + * @param int $colordepthid + * + * @return string + */ public function QuicktimeColorNameLookup($colordepthid) { static $QuicktimeColorNameLookup = array(); if (empty($QuicktimeColorNameLookup)) { @@ -1891,6 +2196,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid'); } + /** + * @param int $stik + * + * @return string + */ public function QuicktimeSTIKLookup($stik) { static $QuicktimeSTIKLookup = array(); if (empty($QuicktimeSTIKLookup)) { @@ -1908,6 +2218,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid'); } + /** + * @param int $audio_profile_id + * + * @return string + */ public function QuicktimeIODSaudioProfileName($audio_profile_id) { static $QuicktimeIODSaudioProfileNameLookup = array(); if (empty($QuicktimeIODSaudioProfileNameLookup)) { @@ -1967,7 +2282,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private'); } - + /** + * @param int $video_profile_id + * + * @return string + */ public function QuicktimeIODSvideoProfileName($video_profile_id) { static $QuicktimeIODSvideoProfileNameLookup = array(); if (empty($QuicktimeIODSvideoProfileNameLookup)) { @@ -2039,7 +2358,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile'); } - + /** + * @param int $rtng + * + * @return string + */ public function QuicktimeContentRatingLookup($rtng) { static $QuicktimeContentRatingLookup = array(); if (empty($QuicktimeContentRatingLookup)) { @@ -2050,6 +2373,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid'); } + /** + * @param int $akid + * + * @return string + */ public function QuicktimeStoreAccountTypeLookup($akid) { static $QuicktimeStoreAccountTypeLookup = array(); if (empty($QuicktimeStoreAccountTypeLookup)) { @@ -2059,6 +2387,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid'); } + /** + * @param int $sfid + * + * @return string + */ public function QuicktimeStoreFrontCodeLookup($sfid) { static $QuicktimeStoreFrontCodeLookup = array(); if (empty($QuicktimeStoreFrontCodeLookup)) { @@ -2088,6 +2421,11 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid'); } + /** + * @param string $atom_data + * + * @return array + */ public function QuicktimeParseNikonNCTG($atom_data) { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 @@ -2130,10 +2468,10 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ ); $offset = 0; + $data = null; $datalength = strlen($atom_data); $parsed = array(); while ($offset < $datalength) { -//echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>'; $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4; $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; @@ -2188,7 +2526,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($ $offset += ($data_size * 2); break; default: -echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>'; + echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>'; break 2; } @@ -2266,7 +2604,13 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br return $parsed; } - + /** + * @param string $keyname + * @param string|array $data + * @param string $boxname + * + * @return bool + */ public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') { static $handyatomtranslatorarray = array(); if (empty($handyatomtranslatorarray)) { @@ -2363,8 +2707,8 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id'; // http://age.hobba.nl/audio/tag_frame_reference.html - $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355 - $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355 + $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355 + $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355 */ } $info = &$this->getid3->info; @@ -2402,6 +2746,50 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br return true; } + /** + * @param string $lstring + * @param int $count + * + * @return string + */ + public function LociString($lstring, &$count) { + // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM + // Also need to return the number of bytes the string occupied so additional fields can be extracted + $len = strlen($lstring); + if ($len == 0) { + $count = 0; + return ''; + } + if ($lstring[0] == "\x00") { + $count = 1; + return ''; + } + //check for BOM + if ($len > 2 && (($lstring[0] == "\xFE" && $lstring[1] == "\xFF") || ($lstring[0] == "\xFF" && $lstring[1] == "\xFE"))) { + //UTF-16 + if (preg_match('/(.*)\x00/', $lstring, $lmatches)){ + $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000 + return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]); + } else { + return ''; + } + } else { + //UTF-8 + if (preg_match('/(.*)\x00/', $lstring, $lmatches)){ + $count = strlen($lmatches[1]) + 1; //account for trailing \x00 + return $lmatches[1]; + }else { + return ''; + } + + } + } + + /** + * @param string $nullterminatedstring + * + * @return string + */ public function NoNullString($nullterminatedstring) { // remove the single null terminator on null terminated strings if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") { @@ -2410,16 +2798,26 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br return $nullterminatedstring; } + /** + * @param string $pascalstring + * + * @return string + */ public function Pascal2String($pascalstring) { // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string return substr($pascalstring, 1); } - /* - // helper functions for m4b audiobook chapters - // code by Steffen Hartmann 2015-Nov-08 - */ + /** + * Helper functions for m4b audiobook chapters + * code by Steffen Hartmann 2015-Nov-08. + * + * @param array $info + * @param string $tag + * @param string $history + * @param array $result + */ public function search_tag_by_key($info, $tag, $history, &$result) { foreach ($info as $key => $value) { $key_history = $history.'/'.$key; @@ -2433,6 +2831,13 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br } } + /** + * @param array $info + * @param string $k + * @param string $v + * @param string $history + * @param array $result + */ public function search_tag_by_pair($info, $k, $v, $history, &$result) { foreach ($info as $key => $value) { $key_history = $history.'/'.$key; @@ -2446,6 +2851,11 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br } } + /** + * @param array $info + * + * @return array + */ public function quicktime_time_to_sample_table($info) { $res = array(); $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res); @@ -2463,7 +2873,12 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br return array(); } - function quicktime_bookmark_time_scale($info) { + /** + * @param array $info + * + * @return int + */ + public function quicktime_bookmark_time_scale($info) { $time_scale = ''; $ts_prefix_len = 0; $res = array(); @@ -2474,10 +2889,10 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br if (count($stbl_res) > 0) { $ts_res = array(); $this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res); - foreach ($ts_res as $value) { - $prefix = substr($value[0], 0, -12); + foreach ($ts_res as $sub_value) { + $prefix = substr($sub_value[0], 0, -12); if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) { - $time_scale = $value[1]['time_scale']; + $time_scale = $sub_value[1]['time_scale']; $ts_prefix_len = strlen($prefix); } } diff --git a/lib/getid3/module.audio-video.real.php b/lib/getid3/module.audio-video.real.php index 280d43c84442d11322b5f40eb57a08a8a03e3fea..f5d2ce7f617b293309e1b6dadde2c2559e61df43 100644 --- a/lib/getid3/module.audio-video.real.php +++ b/lib/getid3/module.audio-video.real.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.real.php // @@ -18,7 +18,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', class getid3_real extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -370,7 +372,12 @@ class getid3_real extends getid3_handler return true; } - + /** + * @param string $OldRAheaderData + * @param array $ParsedArray + * + * @return bool + */ public function ParseOldRAheader($OldRAheaderData, &$ParsedArray) { // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html @@ -485,6 +492,12 @@ class getid3_real extends getid3_handler return true; } + /** + * @param string $fourcc + * @param int $bitrate + * + * @return string + */ public function RealAudioCodecFourCClookup($fourcc, $bitrate) { static $RealAudioCodecFourCClookup = array(); if (empty($RealAudioCodecFourCClookup)) { diff --git a/lib/getid3/module.audio-video.riff.php b/lib/getid3/module.audio-video.riff.php index f61f28abfae9cc15353a9b1e431920364f68e89e..8992ad7c94e08c2e189f4934bd1b5b6fc05a2bf4 100644 --- a/lib/getid3/module.audio-video.riff.php +++ b/lib/getid3/module.audio-video.riff.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.riff.php // @@ -27,10 +27,15 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE_ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true); -class getid3_riff extends getid3_handler { - +class getid3_riff extends getid3_handler +{ protected $container = 'riff'; // default + /** + * @return bool + * + * @throws getid3_exception + */ public function Analyze() { $info = &$this->getid3->info; @@ -46,6 +51,7 @@ class getid3_riff extends getid3_handler { $thisfile_audio_dataformat = &$thisfile_audio['dataformat']; $thisfile_riff_audio = &$thisfile_riff['audio']; $thisfile_riff_video = &$thisfile_riff['video']; + $thisfile_riff_WAVE = array(); $Original['avdataoffset'] = $info['avdataoffset']; $Original['avdataend'] = $info['avdataend']; @@ -357,6 +363,7 @@ class getid3_riff extends getid3_handler { } $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024)); $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772))); + $thisfile_riff['comments']['tag_text'][] = substr($thisfile_riff_WAVE_cart_0['data'], 1772); $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist']; $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title']; @@ -405,7 +412,6 @@ class getid3_riff extends getid3_handler { 'tracktitle'=>'title', 'category' =>'genre', 'cdtitle' =>'album', - 'tracktitle'=>'title', ); foreach ($tagmapping as $fromkey => $tokey) { if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) { @@ -613,6 +619,8 @@ class getid3_riff extends getid3_handler { $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably $thisfile_video['dataformat'] = 'avi'; + $thisfile_riff_video_current = array(); + if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; if (isset($thisfile_riff['AVIX'])) { @@ -701,6 +709,7 @@ class getid3_riff extends getid3_handler { // shortcut $thisfile_riff_video[$streamindex] = array(); + /** @var array $thisfile_riff_video_current */ $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex]; if ($thisfile_riff_raw_avih['dwWidth'] > 0) { @@ -867,7 +876,7 @@ class getid3_riff extends getid3_handler { } } - if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + if (isset($thisfile_riff_raw_strf_strhfccType_streamindex) && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; if (self::fourccLookup($thisfile_video['fourcc'])) { @@ -1077,6 +1086,7 @@ class getid3_riff extends getid3_handler { $thisfile_audio_dataformat = '8svx'; $thisfile_audio['bits_per_sample'] = 8; $thisfile_audio['channels'] = 1; // overridden below, if need be + $ActualBitsPerSample = 0; if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; @@ -1114,7 +1124,7 @@ class getid3_riff extends getid3_handler { break; default: - $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'); + $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.$thisfile_riff_RIFFsubtype_VHDR_0['sCompression'].'"'); break; } } @@ -1174,10 +1184,53 @@ class getid3_riff extends getid3_handler { case 'WEBP': // https://developers.google.com/speed/webp/docs/riff_container + // https://tools.ietf.org/html/rfc6386 + // https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt $info['fileformat'] = 'webp'; $info['mime_type'] = 'image/webp'; -$this->error('WebP image parsing not supported in this version of getID3()'); + if (!empty($thisfile_riff['WEBP']['VP8 '][0]['size'])) { + $old_offset = $this->ftell(); + $this->fseek($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8); // 4 bytes "VP8 " + 4 bytes chunk size + $WEBP_VP8_header = $this->fread(10); + $this->fseek($old_offset); + if (substr($WEBP_VP8_header, 3, 3) == "\x9D\x01\x2A") { + $thisfile_riff['WEBP']['VP8 '][0]['keyframe'] = !(getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x800000); + $thisfile_riff['WEBP']['VP8 '][0]['version'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x700000) >> 20; + $thisfile_riff['WEBP']['VP8 '][0]['show_frame'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x080000); + $thisfile_riff['WEBP']['VP8 '][0]['data_bytes'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x07FFFF) >> 0; + + $thisfile_riff['WEBP']['VP8 '][0]['scale_x'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0xC000) >> 14; + $thisfile_riff['WEBP']['VP8 '][0]['width'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0x3FFF); + $thisfile_riff['WEBP']['VP8 '][0]['scale_y'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0xC000) >> 14; + $thisfile_riff['WEBP']['VP8 '][0]['height'] = (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0x3FFF); + + $info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8 '][0]['width']; + $info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8 '][0]['height']; + } else { + $this->error('Expecting 9D 01 2A at offset '.($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8 + 3).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8_header, 3, 3)).'"'); + } + + } + if (!empty($thisfile_riff['WEBP']['VP8L'][0]['size'])) { + $old_offset = $this->ftell(); + $this->fseek($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8); // 4 bytes "VP8L" + 4 bytes chunk size + $WEBP_VP8L_header = $this->fread(10); + $this->fseek($old_offset); + if (substr($WEBP_VP8L_header, 0, 1) == "\x2F") { + $width_height_flags = getid3_lib::LittleEndian2Bin(substr($WEBP_VP8L_header, 1, 4)); + $thisfile_riff['WEBP']['VP8L'][0]['width'] = bindec(substr($width_height_flags, 18, 14)) + 1; + $thisfile_riff['WEBP']['VP8L'][0]['height'] = bindec(substr($width_height_flags, 4, 14)) + 1; + $thisfile_riff['WEBP']['VP8L'][0]['alpha_is_used'] = (bool) bindec(substr($width_height_flags, 3, 1)); + $thisfile_riff['WEBP']['VP8L'][0]['version'] = bindec(substr($width_height_flags, 0, 3)); + + $info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8L'][0]['width']; + $info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8L'][0]['height']; + } else { + $this->error('Expecting 2F at offset '.($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8L_header, 0, 1)).'"'); + } + + } break; default: @@ -1329,6 +1382,15 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return true; } + /** + * @param int $startoffset + * @param int $maxoffset + * + * @return array|false + * + * @throws Exception + * @throws getid3_exception + */ public function ParseRIFFAMV($startoffset, $maxoffset) { // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size @@ -1437,7 +1499,13 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return $RIFFchunk; } - + /** + * @param int $startoffset + * @param int $maxoffset + * + * @return array|false + * @throws getid3_exception + */ public function ParseRIFF($startoffset, $maxoffset) { $info = &$this->getid3->info; @@ -1689,7 +1757,7 @@ $this->error('WebP image parsing not supported in this version of getID3()'); // break; default: - if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) { + if (!empty($LISTchunkParent) && isset($LISTchunkMaxOffset) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); @@ -1724,6 +1792,11 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return $RIFFchunk; } + /** + * @param string $RIFFdata + * + * @return bool + */ public function ParseRIFFdata(&$RIFFdata) { $info = &$this->getid3->info; if ($RIFFdata) { @@ -1761,6 +1834,12 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return false; } + /** + * @param array $RIFFinfoArray + * @param array $CommentsTargetArray + * + * @return bool + */ public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) { $RIFFinfoKeyLookup = array( 'IARL'=>'archivallocation', @@ -1820,8 +1899,14 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return true; } + /** + * @param string $WaveFormatExData + * + * @return array + */ public static function parseWAVEFORMATex($WaveFormatExData) { // shortcut + $WaveFormatEx = array(); $WaveFormatEx['raw'] = array(); $WaveFormatEx_raw = &$WaveFormatEx['raw']; @@ -1845,6 +1930,11 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return $WaveFormatEx; } + /** + * @param string $WavPackChunkData + * + * @return bool + */ public function parseWavPackHeader($WavPackChunkData) { // typedef struct { // char ckID [4]; @@ -1906,6 +1996,12 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return true; } + /** + * @param string $BITMAPINFOHEADER + * @param bool $littleEndian + * + * @return array + */ public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) { $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure @@ -1925,6 +2021,12 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return $parsed; } + /** + * @param string $DIVXTAG + * @param bool $raw + * + * @return array + */ public static function ParseDIVXTAG($DIVXTAG, $raw=false) { // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/ // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip @@ -1971,6 +2073,7 @@ $this->error('WebP image parsing not supported in this version of getID3()'); 5 => 'NC-17', ); + $parsed = array(); $parsed['title'] = trim(substr($DIVXTAG, 0, 32)); $parsed['artist'] = trim(substr($DIVXTAG, 32, 28)); $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4))); @@ -1986,8 +2089,8 @@ $this->error('WebP image parsing not supported in this version of getID3()'); if (!$raw) { unset($parsed['genre_id'], $parsed['rating_id']); foreach ($parsed as $key => $value) { - if (!$value === '') { - unset($parsed['key']); + if (empty($value)) { + unset($parsed[$key]); } } } @@ -1999,6 +2102,11 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return $parsed; } + /** + * @param string $tagshortname + * + * @return string + */ public static function waveSNDMtagLookup($tagshortname) { $begin = __LINE__; @@ -2022,6 +2130,11 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm'); } + /** + * @param int $wFormatTag + * + * @return string + */ public static function wFormatTagLookup($wFormatTag) { $begin = __LINE__; @@ -2191,6 +2304,11 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag'); } + /** + * @param string $fourcc + * + * @return string + */ public static function fourccLookup($fourcc) { $begin = __LINE__; @@ -2585,6 +2703,12 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc'); } + /** + * @param string $byteword + * @param bool $signed + * + * @return int|float|false + */ private function EitherEndian2Int($byteword, $signed=false) { if ($this->container == 'riff') { return getid3_lib::LittleEndian2Int($byteword, $signed); @@ -2592,4 +2716,4 @@ $this->error('WebP image parsing not supported in this version of getID3()'); return getid3_lib::BigEndian2Int($byteword, false, $signed); } -} \ No newline at end of file +} diff --git a/lib/getid3/module.audio-video.swf.php b/lib/getid3/module.audio-video.swf.php index 178f439a92f213ed91e8e0dcbf1f76955f6b1106..e1151f313755309b017ce068f00440dcb02d9239 100644 --- a/lib/getid3/module.audio-video.swf.php +++ b/lib/getid3/module.audio-video.swf.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.swf.php // @@ -19,6 +19,9 @@ class getid3_swf extends getid3_handler { public $ReturnAllTagData = false; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.audio-video.ts.php b/lib/getid3/module.audio-video.ts.php index 3362024263e6d1ffbaf7ba69589fae5a771e5e74..24679bc75b6d816a8cb91eaf97f019683a1dea2d 100644 --- a/lib/getid3/module.audio-video.ts.php +++ b/lib/getid3/module.audio-video.ts.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio-video.ts.php // @@ -17,7 +17,9 @@ class getid3_ts extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -66,12 +68,16 @@ class getid3_ts extends getid3_handler } } -$this->error('MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; } - + /** + * @param int $raw + * + * @return string + */ public function TSscramblingControlLookup($raw) { $TSscramblingControlLookup = array(0x00=>'not scrambled', 0x01=>'reserved', 0x02=>'scrambled, even key', 0x03=>'scrambled, odd key'); return (isset($TSscramblingControlLookup[$raw]) ? $TSscramblingControlLookup[$raw] : 'invalid'); diff --git a/lib/getid3/module.audio.aa.php b/lib/getid3/module.audio.aa.php index 889fd581831bbab18aa816b7fa72eb3dd18fb7ca..1eec54e23b98a9cdf71a7be9cea19e76ba9da1cc 100644 --- a/lib/getid3/module.audio.aa.php +++ b/lib/getid3/module.audio.aa.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.aa.php // @@ -17,7 +17,9 @@ class getid3_aa extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -36,12 +38,12 @@ class getid3_aa extends getid3_handler $info['fileformat'] = 'aa'; $info['audio']['dataformat'] = 'aa'; -$this->error('Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; $info['audio']['bitrate_mode'] = 'cbr'; // is it? $thisfile_aa['encoding'] = 'ISO-8859-1'; - $thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4)); + $thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AAheader, 0, 4)); if ($thisfile_aa['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) { $this->warning('Possible truncated file - expecting "'.$thisfile_aa['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'); } diff --git a/lib/getid3/module.audio.aac.php b/lib/getid3/module.audio.aac.php index 59d79def6944edb19b834fca9b78c4f87c5639b7..846850cfc11c56b53b2f5d29b607045d4fcf0d6e 100644 --- a/lib/getid3/module.audio.aac.php +++ b/lib/getid3/module.audio.aac.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.aac.php // @@ -17,6 +17,9 @@ class getid3_aac extends getid3_handler { + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); @@ -28,8 +31,9 @@ class getid3_aac extends getid3_handler return true; } - - + /** + * @return bool + */ public function getAACADIFheaderFilepointer() { $info = &$this->getid3->info; $info['fileformat'] = 'aac'; @@ -69,17 +73,17 @@ class getid3_aac extends getid3_handler $bitoffset += 32; $info['aac']['header']['mpeg_version'] = 4; - $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['copyright'] = substr($AACheaderBitstream, $bitoffset, 1) == '1'; $bitoffset += 1; if ($info['aac']['header']['copyright']) { $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); $bitoffset += 72; } - $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['original_copy'] = substr($AACheaderBitstream, $bitoffset, 1) == '1'; $bitoffset += 1; - $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['home'] = substr($AACheaderBitstream, $bitoffset, 1) == '1'; $bitoffset += 1; - $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['is_vbr'] = substr($AACheaderBitstream, $bitoffset, 1) == '1'; $bitoffset += 1; if ($info['aac']['header']['is_vbr']) { $info['audio']['bitrate_mode'] = 'vbr'; @@ -257,7 +261,12 @@ class getid3_aac extends getid3_handler } - + /** + * @param int $MaxFramesToScan + * @param bool $ReturnExtendedInfo + * + * @return bool + */ public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { $info = &$this->getid3->info; @@ -443,6 +452,11 @@ class getid3_aac extends getid3_handler // should never get here. } + /** + * @param int $samplerateid + * + * @return int|string + */ public static function AACsampleRateLookup($samplerateid) { static $AACsampleRateLookup = array(); if (empty($AACsampleRateLookup)) { @@ -466,6 +480,12 @@ class getid3_aac extends getid3_handler return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid'); } + /** + * @param int $profileid + * @param int $mpegversion + * + * @return string + */ public static function AACprofileLookup($profileid, $mpegversion) { static $AACprofileLookup = array(); if (empty($AACprofileLookup)) { @@ -481,6 +501,11 @@ class getid3_aac extends getid3_handler return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid'); } + /** + * @param array $program_configs + * + * @return int + */ public static function AACchannelCountCalculate($program_configs) { $channels = 0; for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) { diff --git a/lib/getid3/module.audio.ac3.php b/lib/getid3/module.audio.ac3.php index c370c7f6bc8cba43d10c4f7bad237b00510c20a1..d4e4e4ba2252754a919687a1cdfa23e9b298d4a5 100644 --- a/lib/getid3/module.audio.ac3.php +++ b/lib/getid3/module.audio.ac3.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.ac3.php // @@ -17,11 +17,21 @@ class getid3_ac3 extends getid3_handler { + /** + * @var array + */ private $AC3header = array(); + + /** + * @var int + */ private $BSIoffset = 0; const syncword = 0x0B77; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -161,21 +171,21 @@ class getid3_ac3 extends getid3_handler if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x01) { $thisfile_ac3_raw_bsi['timecod1'] = $this->readHeaderBSI(14); // 5.4.2.27 timecod1: Time code first half, 14 bits $thisfile_ac3['timecode1'] = 0; - $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x3E00) >> 9) * 3600; // The first 5 bits of this 14-bit field represent the time in hours, with valid values of 0–23 - $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x01F8) >> 3) * 60; // The next 6 bits represent the time in minutes, with valid values of 0–59 - $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x0003) >> 0) * 8; // The final 3 bits represents the time in 8 second increments, with valid values of 0–7 (representing 0, 8, 16, ... 56 seconds) + $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x3E00) >> 9) * 3600; // The first 5 bits of this 14-bit field represent the time in hours, with valid values of 0�23 + $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x01F8) >> 3) * 60; // The next 6 bits represent the time in minutes, with valid values of 0�59 + $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x0003) >> 0) * 8; // The final 3 bits represents the time in 8 second increments, with valid values of 0�7 (representing 0, 8, 16, ... 56 seconds) } if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x02) { $thisfile_ac3_raw_bsi['timecod2'] = $this->readHeaderBSI(14); // 5.4.2.28 timecod2: Time code second half, 14 bits $thisfile_ac3['timecode2'] = 0; - $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x3800) >> 11) * 1; // The first 3 bits of this 14-bit field represent the time in seconds, with valid values from 0–7 (representing 0-7 seconds) - $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x07C0) >> 6) * (1 / 30); // The next 5 bits represents the time in frames, with valid values from 0–29 (one frame = 1/30th of a second) - $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x003F) >> 0) * ((1 / 30) / 60); // The final 6 bits represents fractions of 1/64 of a frame, with valid values from 0–63 + $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x3800) >> 11) * 1; // The first 3 bits of this 14-bit field represent the time in seconds, with valid values from 0�7 (representing 0-7 seconds) + $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x07C0) >> 6) * (1 / 30); // The next 5 bits represents the time in frames, with valid values from 0�29 (one frame = 1/30th of a second) + $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x003F) >> 0) * ((1 / 30) / 60); // The final 6 bits represents fractions of 1/64 of a frame, with valid values from 0�63 } $thisfile_ac3_raw_bsi['flags']['addbsi'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['flags']['addbsi']) { - $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6) + 1; // This 6-bit code, which exists only if addbside is a 1, indicates the length in bytes of additional bit stream information. The valid range of addbsil is 0–63, indicating 1–64 additional bytes, respectively. + $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6) + 1; // This 6-bit code, which exists only if addbside is a 1, indicates the length in bytes of additional bit stream information. The valid range of addbsil is 0�63, indicating 1�64 additional bytes, respectively. $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length'])); @@ -187,7 +197,7 @@ class getid3_ac3 extends getid3_handler } elseif ($thisfile_ac3_raw_bsi['bsid'] <= 16) { // E-AC3 -$this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.'); + $this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.'); $info['audio']['dataformat'] = 'eac3'; $thisfile_ac3_raw_bsi['strmtyp'] = $this->readHeaderBSI(2); @@ -431,15 +441,17 @@ $this->error('E-AC3 parsing is incomplete and experimental in this version of ge $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw_bsi['frmsizecod'], $thisfile_ac3_raw_bsi['fscod']); $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw_bsi['frmsizecod']); } elseif (!empty($thisfile_ac3_raw_bsi['frmsiz'])) { -// this isn't right, but it's (usually) close, roughly 5% less than it should be. -// but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know! + // this isn't right, but it's (usually) close, roughly 5% less than it should be. + // but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know! $thisfile_ac3['bitrate'] = ($thisfile_ac3_raw_bsi['frmsiz'] + 1) * 16 * 30; // The frmsiz field shall contain a value one less than the overall size of the coded syncframe in 16-bit words. That is, this field may assume a value ranging from 0 to 2047, and these values correspond to syncframe sizes ranging from 1 to 2048. -// kludge-fix to make it approximately the expected value, still not "right": -$thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000; + // kludge-fix to make it approximately the expected value, still not "right": + $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000; } $info['audio']['bitrate'] = $thisfile_ac3['bitrate']; - $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); + if (isset($thisfile_ac3_raw_bsi['bsmod']) && isset($thisfile_ac3_raw_bsi['acmod'])) { + $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); + } $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); foreach($ac3_coding_mode as $key => $value) { $thisfile_ac3[$key] = $value; @@ -470,6 +482,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return true; } + /** + * @param int $length + * + * @return float|int + */ private function readHeaderBSI($length) { $data = substr($this->AC3header['bsi'], $this->BSIoffset, $length); $this->BSIoffset += $length; @@ -477,6 +494,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return bindec($data); } + /** + * @param int $fscod + * + * @return int|string|false + */ public static function sampleRateCodeLookup($fscod) { static $sampleRateCodeLookup = array( 0 => 48000, @@ -487,6 +509,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false); } + /** + * @param int $fscod2 + * + * @return int|string|false + */ public static function sampleRateCodeLookup2($fscod2) { static $sampleRateCodeLookup2 = array( 0 => 24000, @@ -497,6 +524,12 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($sampleRateCodeLookup2[$fscod2]) ? $sampleRateCodeLookup2[$fscod2] : false); } + /** + * @param int $bsmod + * @param int $acmod + * + * @return string|false + */ public static function serviceTypeLookup($bsmod, $acmod) { static $serviceTypeLookup = array(); if (empty($serviceTypeLookup)) { @@ -518,6 +551,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false); } + /** + * @param int $acmod + * + * @return array|false + */ public static function audioCodingModeLookup($acmod) { // array(channel configuration, # channels (not incl LFE), channel order) static $audioCodingModeLookup = array ( @@ -533,6 +571,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false); } + /** + * @param int $cmixlev + * + * @return int|float|string|false + */ public static function centerMixLevelLookup($cmixlev) { static $centerMixLevelLookup; if (empty($centerMixLevelLookup)) { @@ -546,6 +589,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false); } + /** + * @param int $surmixlev + * + * @return int|float|string|false + */ public static function surroundMixLevelLookup($surmixlev) { static $surroundMixLevelLookup; if (empty($surroundMixLevelLookup)) { @@ -559,6 +607,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false); } + /** + * @param int $dsurmod + * + * @return string|false + */ public static function dolbySurroundModeLookup($dsurmod) { static $dolbySurroundModeLookup = array( 0 => 'not indicated', @@ -569,12 +622,18 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false); } + /** + * @param int $acmod + * @param bool $lfeon + * + * @return array + */ public static function channelsEnabledLookup($acmod, $lfeon) { $lookup = array( - 'ch1'=>(bool) ($acmod == 0), - 'ch2'=>(bool) ($acmod == 0), - 'left'=>(bool) ($acmod > 1), - 'right'=>(bool) ($acmod > 1), + 'ch1'=>($acmod == 0), + 'ch2'=>($acmod == 0), + 'left'=>($acmod > 1), + 'right'=>($acmod > 1), 'center'=>(bool) ($acmod & 0x01), 'surround_mono'=>false, 'surround_left'=>false, @@ -594,6 +653,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return $lookup; } + /** + * @param int $compre + * + * @return float|int + */ public static function heavyCompression($compre) { // The first four bits indicate gain changes in 6.02dB increments which can be // implemented with an arithmetic shift operation. The following four bits @@ -644,6 +708,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return $log_gain - $lin_gain; } + /** + * @param int $roomtyp + * + * @return string|false + */ public static function roomTypeLookup($roomtyp) { static $roomTypeLookup = array( 0 => 'not indicated', @@ -654,6 +723,12 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false); } + /** + * @param int $frmsizecod + * @param int $fscod + * + * @return int|false + */ public static function frameSizeLookup($frmsizecod, $fscod) { // LSB is whether padding is used or not $padding = (bool) ($frmsizecod & 0x01); @@ -690,6 +765,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false); } + /** + * @param int $frmsizecod + * + * @return int|false + */ public static function bitrateLookup($frmsizecod) { // LSB is whether padding is used or not $padding = (bool) ($frmsizecod & 0x01); @@ -719,6 +799,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16 return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false); } + /** + * @param int $numblkscod + * + * @return int|false + */ public static function blocksPerSyncFrame($numblkscod) { static $blocksPerSyncFrameLookup = array( 0 => 1, diff --git a/lib/getid3/module.audio.amr.php b/lib/getid3/module.audio.amr.php index ff50579675579d5229eaa985f63582d2731b006a..42ebd149947d4b5ea87393a22eaecab960e406b6 100644 --- a/lib/getid3/module.audio.amr.php +++ b/lib/getid3/module.audio.amr.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.aa.php // @@ -17,7 +17,9 @@ class getid3_amr extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -63,7 +65,11 @@ class getid3_amr extends getid3_handler return true; } - + /** + * @param int $key + * + * @return int|false + */ public function amr_mode_bitrate($key) { static $amr_mode_bitrate = array( 0 => 4750, @@ -78,6 +84,11 @@ class getid3_amr extends getid3_handler return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false); } + /** + * @param int $key + * + * @return int|false + */ public function amr_mode_bytes_per_frame($key) { static $amr_mode_bitrate = array( 0 => 13, // 1-byte frame header + 95 bits [padded to: 12 bytes] audio data diff --git a/lib/getid3/module.audio.au.php b/lib/getid3/module.audio.au.php index 075ee8c711697a7a2ba2aae92e92729209a2fdbe..a99d05d7c5e188769de3df40e28a1d2906941906 100644 --- a/lib/getid3/module.audio.au.php +++ b/lib/getid3/module.audio.au.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.au.php // @@ -17,7 +17,9 @@ class getid3_au extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -70,6 +72,11 @@ class getid3_au extends getid3_handler return true; } + /** + * @param int $id + * + * @return string|false + */ public function AUdataFormatNameLookup($id) { static $AUdataFormatNameLookup = array( 0 => 'unspecified format', @@ -104,6 +111,11 @@ class getid3_au extends getid3_handler return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false); } + /** + * @param int $id + * + * @return int|false + */ public function AUdataFormatBitsPerSampleLookup($id) { static $AUdataFormatBitsPerSampleLookup = array( 1 => 8, @@ -132,6 +144,11 @@ class getid3_au extends getid3_handler return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false); } + /** + * @param int $id + * + * @return int|false + */ public function AUdataFormatUsedBitsPerSampleLookup($id) { static $AUdataFormatUsedBitsPerSampleLookup = array( 1 => 8, diff --git a/lib/getid3/module.audio.avr.php b/lib/getid3/module.audio.avr.php index 98666cf0decf64d5a7da61eb2ba5fed49963c564..50e29940b4fbd9a2e88c6da2aabb33c75a0ecd98 100644 --- a/lib/getid3/module.audio.avr.php +++ b/lib/getid3/module.audio.avr.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.avr.php // @@ -17,7 +17,9 @@ class getid3_avr extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.audio.bonk.php b/lib/getid3/module.audio.bonk.php index f314a9f5a4a31a48efe8c8a7a51190544120937f..53983914ff9a9206bc963464a7aedf88f4331980 100644 --- a/lib/getid3/module.audio.bonk.php +++ b/lib/getid3/module.audio.bonk.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.la.php // @@ -17,6 +17,9 @@ class getid3_bonk extends getid3_handler { + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -114,6 +117,9 @@ class getid3_bonk extends getid3_handler } + /** + * @param string $BonkTagName + */ public function HandleBonkTags($BonkTagName) { $info = &$this->getid3->info; switch ($BonkTagName) { @@ -213,6 +219,12 @@ class getid3_bonk extends getid3_handler } } + /** + * @param string $PossibleBonkTag + * @param bool $ignorecase + * + * @return bool + */ public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); foreach ($BonkIsValidTagName as $validtagname) { diff --git a/lib/getid3/module.audio.dsf.php b/lib/getid3/module.audio.dsf.php index 50be37c321bbf7d76bc8f06442c2af0219ce63ae..bbcee76144a9b422a9ba3fd8ad91790b37c6bc15 100644 --- a/lib/getid3/module.audio.dsf.php +++ b/lib/getid3/module.audio.dsf.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.dsf.php // @@ -18,7 +18,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE_ class getid3_dsf extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -115,7 +117,11 @@ class getid3_dsf extends getid3_handler return true; } - + /** + * @param int $channel_type_id + * + * @return string + */ public static function DSFchannelTypeLookup($channel_type_id) { static $DSFchannelTypeLookup = array( // interleaving order: diff --git a/lib/getid3/module.audio.dss.php b/lib/getid3/module.audio.dss.php index 6bd96682796c9fbbcdb35b0efcb98201b9b7bb74..492393a8f77e620cb2d6577befb8737e1fef37e9 100644 --- a/lib/getid3/module.audio.dss.php +++ b/lib/getid3/module.audio.dss.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.dss.php // @@ -17,7 +17,9 @@ class getid3_dss extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -71,6 +73,11 @@ class getid3_dss extends getid3_handler return true; } + /** + * @param string $datestring + * + * @return int|false + */ public function DSSdateStringToUnixDate($datestring) { $y = substr($datestring, 0, 2); $m = substr($datestring, 2, 2); @@ -82,6 +89,11 @@ class getid3_dss extends getid3_handler return mktime($h, $i, $s, $m, $d, $y); } + /** + * @param int $sample_rate_index + * + * @return int|false + */ public function DSSsampleRateLookup($sample_rate_index) { static $dssSampleRateLookup = array( 0x0A => 16000, diff --git a/lib/getid3/module.audio.dts.php b/lib/getid3/module.audio.dts.php index bdc78f0b5e806d988646c323126eb12d170bc1f3..2dd44e9eec55589d0d3aaca46dc2014e54206fbb 100644 --- a/lib/getid3/module.audio.dts.php +++ b/lib/getid3/module.audio.dts.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.dts.php // @@ -21,21 +21,27 @@ class getid3_dts extends getid3_handler { /** - * Default DTS syncword used in native .cpt or .dts formats - */ - const syncword = "\x7F\xFE\x80\x01"; + * Default DTS syncword used in native .cpt or .dts formats. + */ + const syncword = "\x7F\xFE\x80\x01"; + /** + * @var int + */ private $readBinDataOffset = 0; - /** - * Possible syncwords indicating bitstream encoding - */ - public static $syncwords = array( - 0 => "\x7F\xFE\x80\x01", // raw big-endian - 1 => "\xFE\x7F\x01\x80", // raw little-endian - 2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian - 3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian + /** + * Possible syncwords indicating bitstream encoding. + */ + public static $syncwords = array( + 0 => "\x7F\xFE\x80\x01", // raw big-endian + 1 => "\xFE\x7F\x01\x80", // raw little-endian + 2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian + 3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; $info['fileformat'] = 'dts'; @@ -139,6 +145,12 @@ class getid3_dts extends getid3_handler return true; } + /** + * @param string $bin + * @param int $length + * + * @return float|int + */ private function readBinData($bin, $length) { $data = substr($bin, $this->readBinDataOffset, $length); $this->readBinDataOffset += $length; @@ -146,6 +158,11 @@ class getid3_dts extends getid3_handler return bindec($data); } + /** + * @param int $index + * + * @return int|string|false + */ public static function bitrateLookup($index) { static $lookup = array( 0 => 32000, @@ -184,6 +201,11 @@ class getid3_dts extends getid3_handler return (isset($lookup[$index]) ? $lookup[$index] : false); } + /** + * @param int $index + * + * @return int|string|false + */ public static function sampleRateLookup($index) { static $lookup = array( 0 => 'invalid', @@ -206,6 +228,11 @@ class getid3_dts extends getid3_handler return (isset($lookup[$index]) ? $lookup[$index] : false); } + /** + * @param int $index + * + * @return int|false + */ public static function bitPerSampleLookup($index) { static $lookup = array( 0 => 16, @@ -216,6 +243,11 @@ class getid3_dts extends getid3_handler return (isset($lookup[$index]) ? $lookup[$index] : false); } + /** + * @param int $index + * + * @return int|false + */ public static function numChannelsLookup($index) { switch ($index) { case 0: @@ -254,6 +286,11 @@ class getid3_dts extends getid3_handler return false; } + /** + * @param int $index + * + * @return string + */ public static function channelArrangementLookup($index) { static $lookup = array( 0 => 'A', @@ -276,6 +313,12 @@ class getid3_dts extends getid3_handler return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined'); } + /** + * @param int $index + * @param int $version + * + * @return int|false + */ public static function dialogNormalization($index, $version) { switch ($version) { case 7: diff --git a/lib/getid3/module.audio.flac.php b/lib/getid3/module.audio.flac.php index 348cce328d22ff07e95cf24566e30368d40b4571..4edf587df0a2fc52023b708a5eb603e425df25be 100644 --- a/lib/getid3/module.audio.flac.php +++ b/lib/getid3/module.audio.flac.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.flac.php // @@ -24,6 +24,9 @@ class getid3_flac extends getid3_handler { const syncword = 'fLaC'; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -41,22 +44,30 @@ class getid3_flac extends getid3_handler return $this->parseMETAdata(); } + /** + * @return bool + */ public function parseMETAdata() { $info = &$this->getid3->info; do { $BlockOffset = $this->ftell(); $BlockHeader = $this->fread(4); - $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1)); + $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1)); // LBFBT = LastBlockFlag + BlockType $LastBlockFlag = (bool) ($LBFBT & 0x80); $BlockType = ($LBFBT & 0x7F); $BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3)); $BlockTypeText = self::metaBlockTypeLookup($BlockType); if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) { - $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file'); + $this->warning('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file'); break; } if ($BlockLength < 1) { + if ($BlockTypeText != 'reserved') { + // probably supposed to be zero-length + $this->warning('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockTypeText.') at offset '.$BlockOffset.' is zero bytes'); + continue; + } $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid'); break; } @@ -194,12 +205,14 @@ class getid3_flac extends getid3_handler return true; } - private function parseSTREAMINFO($BlockData) { - $info = &$this->getid3->info; - - $info['flac']['STREAMINFO'] = array(); - $streaminfo = &$info['flac']['STREAMINFO']; + /** + * @param string $BlockData + * + * @return array + */ + public static function parseSTREAMINFOdata($BlockData) { + $streaminfo = array(); $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2)); $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2)); $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3)); @@ -211,15 +224,28 @@ class getid3_flac extends getid3_handler $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1; $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36)); - $streaminfo['audio_signature'] = substr($BlockData, 18, 16); + $streaminfo['audio_signature'] = substr($BlockData, 18, 16); + + return $streaminfo; + } + + /** + * @param string $BlockData + * + * @return bool + */ + private function parseSTREAMINFO($BlockData) { + $info = &$this->getid3->info; - if (!empty($streaminfo['sample_rate'])) { + $info['flac']['STREAMINFO'] = self::parseSTREAMINFOdata($BlockData); + + if (!empty($info['flac']['STREAMINFO']['sample_rate'])) { $info['audio']['bitrate_mode'] = 'vbr'; - $info['audio']['sample_rate'] = $streaminfo['sample_rate']; - $info['audio']['channels'] = $streaminfo['channels']; - $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample']; - $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate']; + $info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate']; + $info['audio']['channels'] = $info['flac']['STREAMINFO']['channels']; + $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample']; + $info['playtime_seconds'] = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate']; if ($info['playtime_seconds'] > 0) { if (!$this->isDependencyFor('matroska')) { $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; @@ -236,6 +262,11 @@ class getid3_flac extends getid3_handler return true; } + /** + * @param string $BlockData + * + * @return bool + */ private function parseAPPLICATION($BlockData) { $info = &$this->getid3->info; @@ -246,6 +277,11 @@ class getid3_flac extends getid3_handler return true; } + /** + * @param string $BlockData + * + * @return bool + */ private function parseSEEKTABLE($BlockData) { $info = &$this->getid3->info; @@ -275,6 +311,11 @@ class getid3_flac extends getid3_handler return true; } + /** + * @param string $BlockData + * + * @return bool + */ private function parseVORBIS_COMMENT($BlockData) { $info = &$this->getid3->info; @@ -294,6 +335,11 @@ class getid3_flac extends getid3_handler return true; } + /** + * @param string $BlockData + * + * @return bool + */ private function parseCUESHEET($BlockData) { $info = &$this->getid3->info; $offset = 0; @@ -346,9 +392,11 @@ class getid3_flac extends getid3_handler } /** - * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment - * External usage: audio.ogg - */ + * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment + * External usage: audio.ogg + * + * @return bool + */ public function parsePICTURE() { $info = &$this->getid3->info; @@ -380,6 +428,11 @@ class getid3_flac extends getid3_handler return true; } + /** + * @param int $blocktype + * + * @return string + */ public static function metaBlockTypeLookup($blocktype) { static $lookup = array( 0 => 'STREAMINFO', @@ -393,6 +446,11 @@ class getid3_flac extends getid3_handler return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved'); } + /** + * @param int $applicationid + * + * @return string + */ public static function applicationIDLookup($applicationid) { // http://flac.sourceforge.net/id.html static $lookup = array( @@ -423,6 +481,11 @@ class getid3_flac extends getid3_handler return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved'); } + /** + * @param int $type_id + * + * @return string + */ public static function pictureTypeLookup($type_id) { static $lookup = array ( 0 => 'Other', diff --git a/lib/getid3/module.audio.la.php b/lib/getid3/module.audio.la.php index f46c9aa05d0732f6db2d0dc8fb4c017c99f9a5d1..3fee9d394ad797ac0899809fda23165433b14a2d 100644 --- a/lib/getid3/module.audio.la.php +++ b/lib/getid3/module.audio.la.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.la.php // @@ -18,7 +18,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', class getid3_la extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.audio.lpac.php b/lib/getid3/module.audio.lpac.php index 447cc2d6dc1ed6281eebab393fea493f78250bdd..375ba206577d05a7d8018c31867f9ad12e3bdf0e 100644 --- a/lib/getid3/module.audio.lpac.php +++ b/lib/getid3/module.audio.lpac.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.lpac.php // @@ -18,13 +18,16 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', class getid3_lpac extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); $LPACheader = $this->fread(14); - if (substr($LPACheader, 0, 4) != 'LPAC') { + $StreamMarker = substr($LPACheader, 0, 4); + if ($StreamMarker != 'LPAC') { $this->error('Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"'); return false; } diff --git a/lib/getid3/module.audio.midi.php b/lib/getid3/module.audio.midi.php index 359aca26679ccd56ad42a7891abac75ea235edc9..770481c64043d37c62193471480c3416796b3ab7 100644 --- a/lib/getid3/module.audio.midi.php +++ b/lib/getid3/module.audio.midi.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.midi.php // @@ -19,8 +19,14 @@ define('GETID3_MIDI_MAGIC_MTRK', 'MTrk'); // MIDI track header magic class getid3_midi extends getid3_handler { + /** + * @var bool + */ public $scanwholefile = true; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -88,6 +94,7 @@ class getid3_midi extends getid3_handler $CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat $CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat $MicroSecondsPerQuarterNoteAfter = array (); + $MIDIevents = array(); foreach ($trackdataarray as $tracknumber => $trackdata) { @@ -283,7 +290,8 @@ class getid3_midi extends getid3_handler $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime); } } - $previoustickoffset = null; + $previoustickoffset = null; + $prevmicrosecondsperbeat = null; ksort($MicroSecondsPerQuarterNoteAfter); foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) { @@ -312,7 +320,7 @@ class getid3_midi extends getid3_handler return false; } - $info['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000); + $info['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000); } } @@ -329,6 +337,11 @@ class getid3_midi extends getid3_handler return true; } + /** + * @param int $instrumentid + * + * @return string + */ public function GeneralMIDIinstrumentLookup($instrumentid) { $begin = __LINE__; @@ -469,6 +482,11 @@ class getid3_midi extends getid3_handler return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument'); } + /** + * @param int $instrumentid + * + * @return string + */ public function GeneralMIDIpercussionLookup($instrumentid) { $begin = __LINE__; diff --git a/lib/getid3/module.audio.mod.php b/lib/getid3/module.audio.mod.php index 4b888ecdabe5b433614b0708f4da205461cafe4f..708d213b4eef8cabbae9230c2f0d3261daacb735 100644 --- a/lib/getid3/module.audio.mod.php +++ b/lib/getid3/module.audio.mod.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.mod.php // @@ -17,7 +17,9 @@ class getid3_mod extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); @@ -35,7 +37,9 @@ class getid3_mod extends getid3_handler return false; } - + /** + * @return bool + */ public function getMODheaderFilepointer() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset'] + 1080); @@ -51,6 +55,9 @@ class getid3_mod extends getid3_handler return false; } + /** + * @return bool + */ public function getXMheaderFilepointer() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); @@ -66,6 +73,9 @@ class getid3_mod extends getid3_handler return false; } + /** + * @return bool + */ public function getS3MheaderFilepointer() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset'] + 44); @@ -81,6 +91,9 @@ class getid3_mod extends getid3_handler return false; } + /** + * @return bool + */ public function getITheaderFilepointer() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); diff --git a/lib/getid3/module.audio.monkey.php b/lib/getid3/module.audio.monkey.php index afa2eaf27e584759f55db2aa1832ae4c5a1fd515..3317a93efa83c015386e7ac8411ee58e968a5001 100644 --- a/lib/getid3/module.audio.monkey.php +++ b/lib/getid3/module.audio.monkey.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.monkey.php // @@ -17,7 +17,9 @@ class getid3_monkey extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -177,6 +179,11 @@ class getid3_monkey extends getid3_handler return true; } + /** + * @param int $compressionlevel + * + * @return string + */ public function MonkeyCompressionLevelNameLookup($compressionlevel) { static $MonkeyCompressionLevelNameLookup = array( 0 => 'unknown', @@ -189,6 +196,12 @@ class getid3_monkey extends getid3_handler return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid'); } + /** + * @param int $versionid + * @param int $compressionlevel + * + * @return int + */ public function MonkeySamplesPerFrame($versionid, $compressionlevel) { if ($versionid >= 3950) { return 73728 * 4; diff --git a/lib/getid3/module.audio.mp3.php b/lib/getid3/module.audio.mp3.php index ca3ec5452f387f39801205007685093efef14c2f..4434380455cfc7f4274f5ced2a993c94fa0fc4e9 100644 --- a/lib/getid3/module.audio.mp3.php +++ b/lib/getid3/module.audio.mp3.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.mp3.php // @@ -24,9 +24,17 @@ define('GETID3_MP3_VALID_CHECK_FRAMES', 35); class getid3_mp3 extends getid3_handler { - - public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files - + /** + * Forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, + * unrecommended, but may provide data from otherwise-unusable files. + * + * @var bool + */ + public $allow_bruteforce = false; + + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -35,7 +43,7 @@ class getid3_mp3 extends getid3_handler if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) { if ($this->allow_bruteforce) { $this->error('Rescanning file in BruteForce mode'); - $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info); + $this->getOnlyMPEGaudioInfoBruteForce(); } } @@ -152,7 +160,11 @@ class getid3_mp3 extends getid3_handler // Calculate playtime if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) { - $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; + // https://github.com/JamesHeinrich/getID3/issues/161 + // VBR header frame contains ~0.026s of silent audio data, but is not actually part of the original encoding and should be ignored + $xingVBRheaderFrameLength = ((isset($info['mpeg']['audio']['VBR_frames']) && isset($info['mpeg']['audio']['framelength'])) ? $info['mpeg']['audio']['framelength'] : 0); + + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset'] - $xingVBRheaderFrameLength) * 8 / $info['audio']['bitrate']; } $info['audio']['encoder_options'] = $this->GuessEncoderOptions(); @@ -160,10 +172,14 @@ class getid3_mp3 extends getid3_handler return true; } - + /** + * @return string + */ public function GuessEncoderOptions() { // shortcuts $info = &$this->getid3->info; + $thisfile_mpeg_audio = array(); + $thisfile_mpeg_audio_lame = array(); if (!empty($info['mpeg']['audio'])) { $thisfile_mpeg_audio = &$info['mpeg']['audio']; if (!empty($thisfile_mpeg_audio['LAME'])) { @@ -178,7 +194,7 @@ class getid3_mp3 extends getid3_handler $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; - } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { + } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && isset($thisfile_mpeg_audio_lame['preset_used_id']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; @@ -404,7 +420,15 @@ class getid3_mp3 extends getid3_handler return $encoder_options; } - + /** + * @param int $offset + * @param array $info + * @param bool $recursivesearch + * @param bool $ScanAsCBR + * @param bool $FastMPEGheaderScan + * + * @return bool + */ public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; @@ -437,18 +461,19 @@ class getid3_mp3 extends getid3_handler // and $cc... is the audio data $head4 = substr($headerstring, 0, 4); + $head4_key = getid3_lib::PrintHexBytes($head4, true, false, false); static $MPEGaudioHeaderDecodeCache = array(); - if (isset($MPEGaudioHeaderDecodeCache[$head4])) { - $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; + if (isset($MPEGaudioHeaderDecodeCache[$head4_key])) { + $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4_key]; } else { $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4); - $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; + $MPEGaudioHeaderDecodeCache[$head4_key] = $MPEGheaderRawArray; } static $MPEGaudioHeaderValidCache = array(); - if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache - //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) - $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); + if (!isset($MPEGaudioHeaderValidCache[$head4_key])) { // Not in cache + //$MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) + $MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); } // shortcut @@ -457,8 +482,7 @@ class getid3_mp3 extends getid3_handler } $thisfile_mpeg_audio = &$info['mpeg']['audio']; - - if ($MPEGaudioHeaderValidCache[$head4]) { + if ($MPEGaudioHeaderValidCache[$head4_key]) { $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; } else { $this->error('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset); @@ -561,7 +585,7 @@ class getid3_mp3 extends getid3_handler $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; - $info['audio']['codec'] = 'Fraunhofer'; + $info['audio']['codec'] = 'Fraunhofer'; $SideInfoData = substr($headerstring, 4 + 2, 32); @@ -654,7 +678,7 @@ class getid3_mp3 extends getid3_handler $used_filesize = $thisfile_mpeg_audio['VBR_bytes']; } elseif (!empty($info['filesize'])) { $used_filesize = $info['filesize']; - $used_filesize -= intval(@$info['id3v2']['headerlength']); + $used_filesize -= (isset($info['id3v2']['headerlength']) ? intval($info['id3v2']['headerlength']) : 0); $used_filesize -= (isset($info['id3v1']) ? 128 : 0); $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0); $this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes'); @@ -1082,6 +1106,13 @@ class getid3_mp3 extends getid3_handler return true; } + /** + * @param int $offset + * @param int $nextframetestoffset + * @param bool $ScanAsCBR + * + * @return bool + */ public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { $info = &$this->getid3->info; $firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']); @@ -1128,6 +1159,12 @@ class getid3_mp3 extends getid3_handler return true; } + /** + * @param int $offset + * @param bool $deepscan + * + * @return int|false + */ public function FreeFormatFrameLength($offset, $deepscan=false) { $info = &$this->getid3->info; @@ -1205,6 +1242,9 @@ class getid3_mp3 extends getid3_handler return $framelength; } + /** + * @return bool + */ public function getOnlyMPEGaudioInfoBruteForce() { $MPEGaudioHeaderDecodeCache = array(); $MPEGaudioHeaderValidCache = array(); @@ -1352,7 +1392,12 @@ class getid3_mp3 extends getid3_handler return true; } - + /** + * @param int $avdataoffset + * @param bool $BitrateHistogram + * + * @return bool + */ public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) { // looks for synch, decodes MPEG audio header @@ -1416,6 +1461,7 @@ class getid3_mp3 extends getid3_handler } if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected + $FirstFrameAVDataOffset = null; if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) { $FirstFrameThisfileInfo = $info; $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; @@ -1439,7 +1485,7 @@ class getid3_mp3 extends getid3_handler $info['audio']['dataformat'] = 'mp3'; break; } - if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { + if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { // If there is garbage data between a valid VBR header frame and a sequence // of valid MPEG-audio frames the VBR data is no longer discarded. @@ -1521,7 +1567,7 @@ class getid3_mp3 extends getid3_handler } } $synchstartoffset = $scan_start_offset[$current_segment]; - while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) { + while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) { $FastMode = true; $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; @@ -1632,17 +1678,25 @@ class getid3_mp3 extends getid3_handler return true; } - + /** + * @return array + */ public static function MPEGaudioVersionArray() { static $MPEGaudioVersion = array('2.5', false, '2', '1'); return $MPEGaudioVersion; } + /** + * @return array + */ public static function MPEGaudioLayerArray() { static $MPEGaudioLayer = array(false, 3, 2, 1); return $MPEGaudioLayer; } + /** + * @return array + */ public static function MPEGaudioBitrateArray() { static $MPEGaudioBitrate; if (empty($MPEGaudioBitrate)) { @@ -1662,6 +1716,9 @@ class getid3_mp3 extends getid3_handler return $MPEGaudioBitrate; } + /** + * @return array + */ public static function MPEGaudioFrequencyArray() { static $MPEGaudioFrequency; if (empty($MPEGaudioFrequency)) { @@ -1674,11 +1731,17 @@ class getid3_mp3 extends getid3_handler return $MPEGaudioFrequency; } + /** + * @return array + */ public static function MPEGaudioChannelModeArray() { static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); return $MPEGaudioChannelMode; } + /** + * @return array + */ public static function MPEGaudioModeExtensionArray() { static $MPEGaudioModeExtension; if (empty($MPEGaudioModeExtension)) { @@ -1691,15 +1754,31 @@ class getid3_mp3 extends getid3_handler return $MPEGaudioModeExtension; } + /** + * @return array + */ public static function MPEGaudioEmphasisArray() { static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); return $MPEGaudioEmphasis; } + /** + * @param string $head4 + * @param bool $allowBitrate15 + * + * @return bool + */ public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) { return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15); } + /** + * @param array $rawarray + * @param bool $echoerrors + * @param bool $allowBitrate15 + * + * @return bool + */ public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) { if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { return false; @@ -1772,6 +1851,11 @@ class getid3_mp3 extends getid3_handler return true; } + /** + * @param string $Header4Bytes + * + * @return array|false + */ public static function MPEGaudioHeaderDecode($Header4Bytes) { // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM // A - Frame sync (all bits set) @@ -1809,6 +1893,15 @@ class getid3_mp3 extends getid3_handler return $MPEGrawHeader; } + /** + * @param int|string $bitrate + * @param string $version + * @param string $layer + * @param bool $padding + * @param int $samplerate + * + * @return int|false + */ public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { static $AudioFrameLengthCache = array(); @@ -1870,6 +1963,11 @@ class getid3_mp3 extends getid3_handler return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; } + /** + * @param float|int $bit_rate + * + * @return int|float|string + */ public static function ClosestStandardMP3Bitrate($bit_rate) { static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); static $bit_rate_table = array (0=>'-'); @@ -1890,10 +1988,16 @@ class getid3_mp3 extends getid3_handler return $bit_rate_table[$round_bit_rate]; } + /** + * @param string $version + * @param string $channelmode + * + * @return int + */ public static function XingVBRidOffset($version, $channelmode) { static $XingVBRidOffsetCache = array(); - if (empty($XingVBRidOffset)) { - $XingVBRidOffset = array ( + if (empty($XingVBRidOffsetCache)) { + $XingVBRidOffsetCache = array ( '1' => array ('mono' => 0x15, // 4 + 17 = 21 'stereo' => 0x24, // 4 + 32 = 36 'joint stereo' => 0x24, @@ -1913,9 +2017,14 @@ class getid3_mp3 extends getid3_handler ) ); } - return $XingVBRidOffset[$version][$channelmode]; + return $XingVBRidOffsetCache[$version][$channelmode]; } + /** + * @param int $VBRmethodID + * + * @return string + */ public static function LAMEvbrMethodLookup($VBRmethodID) { static $LAMEvbrMethodLookup = array( 0x00 => 'unknown', @@ -1932,6 +2041,11 @@ class getid3_mp3 extends getid3_handler return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); } + /** + * @param int $StereoModeID + * + * @return string + */ public static function LAMEmiscStereoModeLookup($StereoModeID) { static $LAMEmiscStereoModeLookup = array( 0 => 'mono', @@ -1946,6 +2060,11 @@ class getid3_mp3 extends getid3_handler return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); } + /** + * @param int $SourceSampleFrequencyID + * + * @return string + */ public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { static $LAMEmiscSourceSampleFrequencyLookup = array( 0 => '<= 32 kHz', @@ -1956,6 +2075,11 @@ class getid3_mp3 extends getid3_handler return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); } + /** + * @param int $SurroundInfoID + * + * @return string + */ public static function LAMEsurroundInfoLookup($SurroundInfoID) { static $LAMEsurroundInfoLookup = array( 0 => 'no surround info', @@ -1966,6 +2090,11 @@ class getid3_mp3 extends getid3_handler return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); } + /** + * @param array $LAMEtag + * + * @return string + */ public static function LAMEpresetUsedLookup($LAMEtag) { if ($LAMEtag['preset_used_id'] == 0) { diff --git a/lib/getid3/module.audio.mpc.php b/lib/getid3/module.audio.mpc.php index 14e829d97323a00fa889ea3eb6fb6bf5409394cf..6d7c8512015f0ea7ee34ce086267bff8d9bf14a1 100644 --- a/lib/getid3/module.audio.mpc.php +++ b/lib/getid3/module.audio.mpc.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.mpc.php // @@ -17,7 +17,9 @@ class getid3_mpc extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -56,10 +58,11 @@ class getid3_mpc extends getid3_handler return false; } - return false; } - + /** + * @return bool + */ public function ParseMPCsv8() { // this is SV8 // http://trac.musepack.net/trac/wiki/SV8Specification @@ -208,6 +211,9 @@ class getid3_mpc extends getid3_handler return true; } + /** + * @return bool + */ public function ParseMPCsv7() { // this is SV7 // http://www.uni-jena.de/~pfk/mpp/sv8/header.html @@ -322,6 +328,9 @@ class getid3_mpc extends getid3_handler return true; } + /** + * @return bool + */ public function ParseMPCsv6() { // this is SV4 - SV6 @@ -397,7 +406,11 @@ class getid3_mpc extends getid3_handler return true; } - + /** + * @param int $profileid + * + * @return string + */ public function MPCprofileNameLookup($profileid) { static $MPCprofileNameLookup = array( 0 => 'no profile', @@ -420,6 +433,11 @@ class getid3_mpc extends getid3_handler return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid'); } + /** + * @param int $frequencyid + * + * @return int|string + */ public function MPCfrequencyLookup($frequencyid) { static $MPCfrequencyLookup = array( 0 => 44100, @@ -430,6 +448,11 @@ class getid3_mpc extends getid3_handler return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid'); } + /** + * @param int $intvalue + * + * @return float|false + */ public function MPCpeakDBLookup($intvalue) { if ($intvalue > 0) { return ((log10($intvalue) / log10(2)) - 15) * 6; @@ -437,6 +460,11 @@ class getid3_mpc extends getid3_handler return false; } + /** + * @param int $encoderversion + * + * @return string + */ public function MPCencoderVersionLookup($encoderversion) { //Encoder version * 100 (106 = 1.06) //EncoderVersion % 10 == 0 Release (1.0) @@ -464,6 +492,13 @@ class getid3_mpc extends getid3_handler return number_format($encoderversion / 100, 2).' alpha'; } + /** + * @param string $data + * @param int $packetLength + * @param int $maxHandledPacketLength + * + * @return int|false + */ public function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) { $packet_size = 0; for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) { @@ -488,6 +523,11 @@ class getid3_mpc extends getid3_handler return $packet_size; } + /** + * @param string $packetKey + * + * @return string + */ public function MPCsv8PacketName($packetKey) { static $MPCsv8PacketName = array(); if (empty($MPCsv8PacketName)) { diff --git a/lib/getid3/module.audio.ogg.php b/lib/getid3/module.audio.ogg.php index e41c96c4ba4acbc89c76b115e0cb98f8a82c6024..51fee3e1ce77152dc14342c920f7555aefe1197e 100644 --- a/lib/getid3/module.audio.ogg.php +++ b/lib/getid3/module.audio.ogg.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.ogg.php // @@ -18,7 +18,11 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE class getid3_ogg extends getid3_handler { - // http://xiph.org/vorbis/doc/Vorbis_I_spec.html + /** + * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html + * + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -65,7 +69,7 @@ class getid3_ogg extends getid3_handler } elseif (substr($filedata, 0, 8) == 'OpusHead') { - if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) { + if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) { return false; } @@ -179,7 +183,7 @@ class getid3_ogg extends getid3_handler if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) { $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator']; } -$this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable'); + $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable'); } elseif (substr($filedata, 0, 8) == "fishead\x00") { @@ -259,9 +263,34 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' $this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']'); //return false; + } elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') { + // https://xiph.org/flac/ogg_mapping.html + + $info['audio']['dataformat'] = 'flac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; + + $info['ogg']['flac']['header']['version_major'] = ord(substr($filedata, 5, 1)); + $info['ogg']['flac']['header']['version_minor'] = ord(substr($filedata, 6, 1)); + $info['ogg']['flac']['header']['header_packets'] = getid3_lib::BigEndian2Int(substr($filedata, 7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams." + $info['ogg']['flac']['header']['magic'] = substr($filedata, 9, 4); + if ($info['ogg']['flac']['header']['magic'] != 'fLaC') { + $this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')'); + return false; + } + $info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4)); + $info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34)); + if (!empty($info['flac']['STREAMINFO']['sample_rate'])) { + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate']; + $info['audio']['channels'] = $info['flac']['STREAMINFO']['channels']; + $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample']; + $info['playtime_seconds'] = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate']; + } + } else { - $this->error('Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"'); + $this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"'); unset($info['ogg']); unset($info['mime_type']); return false; @@ -378,6 +407,13 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return true; } + /** + * @param string $filedata + * @param int $filedataoffset + * @param array $oggpageinfo + * + * @return bool + */ public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) { $info = &$this->getid3->info; $info['audio']['dataformat'] = 'vorbis'; @@ -426,7 +462,15 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return true; } - // http://tools.ietf.org/html/draft-ietf-codec-oggopus-03 + /** + * @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03 + * + * @param string $filedata + * @param int $filedataoffset + * @param array $oggpageinfo + * + * @return bool + */ public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) { $info = &$this->getid3->info; $info['audio']['dataformat'] = 'opus'; @@ -458,7 +502,7 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' $info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); $filedataoffset += 2; - $info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; //$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); @@ -467,16 +511,19 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' //$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); //$filedataoffset += 1; - $info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version']; - $info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate']; - $info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count']; + $info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version']; + $info['opus']['sample_rate_input'] = $info['ogg']['pageheader']['opus']['input_sample_rate']; + $info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count']; - $info['audio']['channels'] = $info['opus']['out_channel_count']; - $info['audio']['sample_rate'] = $info['opus']['sample_rate']; + $info['audio']['channels'] = $info['opus']['out_channel_count']; + $info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input']; + $info['audio']['sample_rate'] = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html return true; } - + /** + * @return array|false + */ public function ParseOggPageHeader() { // http://xiph.org/ogg/vorbis/doc/framing.html $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file @@ -489,7 +536,7 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return false; } if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { - if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) { + if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) { // get some more data, unless eof, in which case fail return false; } @@ -528,13 +575,19 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return $oggheader; } - // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005 + /** + * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005 + * + * @return bool + */ public function ParseVorbisComments() { $info = &$this->getid3->info; $OriginalOffset = $this->ftell(); + $commentdata = null; $commentdataoffset = 0; $VorbisCommentPage = 1; + $CommentStartOffset = 0; switch ($info['audio']['dataformat']) { case 'vorbis': @@ -765,6 +818,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return true; } + /** + * @param int $mode + * + * @return string|null + */ public static function SpeexBandModeLookup($mode) { static $SpeexBandModeLookup = array(); if (empty($SpeexBandModeLookup)) { @@ -775,8 +833,14 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null); } - + /** + * @param array $OggInfoArray + * @param int $SegmentNumber + * + * @return int + */ public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) { + $segmentlength = 0; for ($i = 0; $i < $SegmentNumber; $i++) { $segmentlength = 0; foreach ($OggInfoArray['segment_table'] as $key => $value) { @@ -789,7 +853,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return $segmentlength; } - + /** + * @param int $nominal_bitrate + * + * @return float + */ public static function get_quality_from_nominal_bitrate($nominal_bitrate) { // decrease precision @@ -813,6 +881,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return round($qval, 1); // 5 or 4.9 } + /** + * @param int $colorspace_id + * + * @return string|null + */ public static function TheoraColorSpace($colorspace_id) { // http://www.theora.org/doc/Theora.pdf (table 6.3) static $TheoraColorSpaceLookup = array(); @@ -825,6 +898,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null); } + /** + * @param int $pixelformat_id + * + * @return string|null + */ public static function TheoraPixelFormat($pixelformat_id) { // http://www.theora.org/doc/Theora.pdf (table 6.4) static $TheoraPixelFormatLookup = array(); diff --git a/lib/getid3/module.audio.optimfrog.php b/lib/getid3/module.audio.optimfrog.php index 50e0ffd6da0252a468c4103133db5b1eab8bda05..1c12230558cc3834d91ca59d61e708246d661500 100644 --- a/lib/getid3/module.audio.optimfrog.php +++ b/lib/getid3/module.audio.optimfrog.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.optimfrog.php // @@ -18,7 +18,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', class getid3_optimfrog extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -44,7 +46,9 @@ class getid3_optimfrog extends getid3_handler return false; } - + /** + * @return bool + */ public function ParseOptimFROGheader42() { // for fileformat of v4.21 and older @@ -91,7 +95,9 @@ class getid3_optimfrog extends getid3_handler return true; } - + /** + * @return bool + */ public function ParseOptimFROGheader45() { // for fileformat of v4.50a and higher @@ -313,7 +319,11 @@ class getid3_optimfrog extends getid3_handler return true; } - + /** + * @param int $SampleType + * + * @return string|false + */ public static function OptimFROGsampleTypeLookup($SampleType) { static $OptimFROGsampleTypeLookup = array( 0 => 'unsigned int (8-bit)', @@ -331,6 +341,11 @@ class getid3_optimfrog extends getid3_handler return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false); } + /** + * @param int $SampleType + * + * @return int|false + */ public static function OptimFROGbitsPerSampleTypeLookup($SampleType) { static $OptimFROGbitsPerSampleTypeLookup = array( 0 => 8, @@ -348,6 +363,11 @@ class getid3_optimfrog extends getid3_handler return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false); } + /** + * @param int $ChannelConfiguration + * + * @return string|false + */ public static function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { static $OptimFROGchannelConfigurationLookup = array( 0 => 'mono', @@ -356,6 +376,11 @@ class getid3_optimfrog extends getid3_handler return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false); } + /** + * @param int $ChannelConfiguration + * + * @return int|false + */ public static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { static $OptimFROGchannelConfigNumChannelsLookup = array( 0 => 1, @@ -365,13 +390,17 @@ class getid3_optimfrog extends getid3_handler } - // static function OptimFROGalgorithmNameLookup($AlgorithID) { // static $OptimFROGalgorithmNameLookup = array(); // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false); // } + /** + * @param int $EncoderID + * + * @return string + */ public static function OptimFROGencoderNameLookup($EncoderID) { // version = (encoderID >> 4) + 4500 // system = encoderID & 0xF @@ -387,6 +416,11 @@ class getid3_optimfrog extends getid3_handler return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')'; } + /** + * @param int $CompressionID + * + * @return string + */ public static function OptimFROGcompressionLookup($CompressionID) { // mode = compression >> 3 // speedup = compression & 0x07 @@ -409,6 +443,11 @@ class getid3_optimfrog extends getid3_handler return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')'); } + /** + * @param int $CompressionID + * + * @return string + */ public static function OptimFROGspeedupLookup($CompressionID) { // mode = compression >> 3 // speedup = compression & 0x07 diff --git a/lib/getid3/module.audio.rkau.php b/lib/getid3/module.audio.rkau.php index d7c2f09ad7695c11d0b64e6f3a4bc9658e167e2b..7e1c810724d87060d0ad8f6fdba2f58503be6cfd 100644 --- a/lib/getid3/module.audio.rkau.php +++ b/lib/getid3/module.audio.rkau.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.shorten.php // @@ -17,7 +17,9 @@ class getid3_rkau extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -50,7 +52,7 @@ class getid3_rkau extends getid3_handler $this->RKAUqualityLookup($info['rkau']); $info['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1)); - $info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x01)); + $info['rkau']['flags']['joint_stereo'] = !($info['rkau']['raw']['flags'] & 0x01); $info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02); $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04); @@ -76,7 +78,11 @@ class getid3_rkau extends getid3_handler } - + /** + * @param array $RKAUdata + * + * @return bool + */ public function RKAUqualityLookup(&$RKAUdata) { $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4; $quality = $RKAUdata['raw']['quality'] & 0x0F; diff --git a/lib/getid3/module.audio.shorten.php b/lib/getid3/module.audio.shorten.php index 8d5c5d4bed9223cd8528def8f9aeff13e3db8730..f5e2a57d787544c706bc278c63d0615f07e66608 100644 --- a/lib/getid3/module.audio.shorten.php +++ b/lib/getid3/module.audio.shorten.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.shorten.php // @@ -17,7 +17,9 @@ class getid3_shorten extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -38,7 +40,7 @@ class getid3_shorten extends getid3_handler $this->fseek($info['avdataend'] - 12); $SeekTableSignatureTest = $this->fread(12); - $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); + $info['shn']['seektable']['present'] = substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'; if ($info['shn']['seektable']['present']) { $info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length']; diff --git a/lib/getid3/module.audio.tta.php b/lib/getid3/module.audio.tta.php index 78d27b0f31c46aa5b7ab0e9b3327874979bc27ec..13d8fe2c1facbdb76b9f7a9493360c69691f1826 100644 --- a/lib/getid3/module.audio.tta.php +++ b/lib/getid3/module.audio.tta.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.tta.php // @@ -17,7 +17,9 @@ class getid3_tta extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.audio.voc.php b/lib/getid3/module.audio.voc.php index 3803d8a168f738ea90d740e4439853de47ae2a71..448315811bff44f4312c6dad91a30e4ad7a2ad15 100644 --- a/lib/getid3/module.audio.voc.php +++ b/lib/getid3/module.audio.voc.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.voc.php // @@ -17,7 +17,9 @@ class getid3_voc extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -164,6 +166,11 @@ class getid3_voc extends getid3_handler return true; } + /** + * @param int $index + * + * @return string + */ public function VOCcompressionTypeLookup($index) { static $VOCcompressionTypeLookup = array( 0 => '8-bit', @@ -174,6 +181,11 @@ class getid3_voc extends getid3_handler return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); } + /** + * @param int $index + * + * @return string|false + */ public function VOCwFormatLookup($index) { static $VOCwFormatLookup = array( 0x0000 => '8-bit unsigned PCM', @@ -188,6 +200,11 @@ class getid3_voc extends getid3_handler return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); } + /** + * @param int $index + * + * @return int|false + */ public function VOCwFormatActualBitsPerSampleLookup($index) { static $VOCwFormatLookup = array( 0x0000 => 8, diff --git a/lib/getid3/module.audio.vqf.php b/lib/getid3/module.audio.vqf.php index a6a391c40d09a76c820348f6f4f30a26147d775f..9ab821e2d9a0d090226389bf2b2af8b99756bce4 100644 --- a/lib/getid3/module.audio.vqf.php +++ b/lib/getid3/module.audio.vqf.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.vqf.php // @@ -17,6 +17,9 @@ class getid3_vqf extends getid3_handler { + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -136,6 +139,11 @@ class getid3_vqf extends getid3_handler return true; } + /** + * @param int $frequencyid + * + * @return int + */ public function VQFchannelFrequencyLookup($frequencyid) { static $VQFchannelFrequencyLookup = array( 11 => 11025, @@ -145,6 +153,11 @@ class getid3_vqf extends getid3_handler return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000); } + /** + * @param string $shortname + * + * @return string + */ public function VQFcommentNiceNameLookup($shortname) { static $VQFcommentNiceNameLookup = array( 'NAME' => 'title', diff --git a/lib/getid3/module.audio.wavpack.php b/lib/getid3/module.audio.wavpack.php index b54c179f3d89cd7ad84a706bfe6f5b8c7aefa882..a6e57fd93fc3df01ab57f637d173eefda9d5dcb5 100644 --- a/lib/getid3/module.audio.wavpack.php +++ b/lib/getid3/module.audio.wavpack.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.audio.wavpack.php // @@ -17,7 +17,9 @@ class getid3_wavpack extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -236,7 +238,7 @@ class getid3_wavpack extends getid3_handler case 0x22: // ID_RIFF_TRAILER - $metablockRIFFfooter = $metablockRIFFheader.$metablock['data']; + $metablockRIFFfooter = isset($metablockRIFFheader) ? $metablockRIFFheader : ''.$metablock['data']; getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); @@ -368,7 +370,11 @@ class getid3_wavpack extends getid3_handler return true; } - + /** + * @param int $id + * + * @return string + */ public function WavPackMetablockNameLookup(&$id) { static $WavPackMetablockNameLookup = array( 0x00 => 'Dummy', diff --git a/lib/getid3/module.graphic.bmp.php b/lib/getid3/module.graphic.bmp.php index 90cbb38f0b620467a175b45069865ed8a91b34ec..dc7b30d727e4b434952cfe85388f0dc1f0db5d33 100644 --- a/lib/getid3/module.graphic.bmp.php +++ b/lib/getid3/module.graphic.bmp.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.bmp.php // @@ -20,6 +20,9 @@ class getid3_bmp extends getid3_handler public $ExtractPalette = false; public $ExtractData = false; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -528,6 +531,7 @@ class getid3_bmp extends getid3_handler // high- and low-order 4 bits, one color index for each pixel. In absolute mode, // each run must be aligned on a word boundary. unset($paletteindexes); + $paletteindexes = array(); for ($i = 0; $i < ceil($secondbyte / 2); $i++) { $paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4; @@ -625,7 +629,11 @@ class getid3_bmp extends getid3_handler return true; } - + /** + * @param array $BMPinfo + * + * @return bool + */ public function PlotBMP(&$BMPinfo) { $starttime = time(); if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) { @@ -633,15 +641,15 @@ class getid3_bmp extends getid3_handler return false; } set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000))); - if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) { + if ($im = imagecreatetruecolor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) { for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) { for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) { if (isset($BMPinfo['bmp']['data'][$row][$col])) { $red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16; $green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8; $blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF); - $pixelcolor = ImageColorAllocate($im, $red, $green, $blue); - ImageSetPixel($im, $col, $row, $pixelcolor); + $pixelcolor = imagecolorallocate($im, $red, $green, $blue); + imagesetpixel($im, $col, $row, $pixelcolor); } else { //echo 'ERROR: no data for pixel '.$row.' x '.$col.'<BR>'; //return false; @@ -650,18 +658,23 @@ class getid3_bmp extends getid3_handler } if (headers_sent()) { echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>'; - ImageDestroy($im); + imagedestroy($im); exit; } else { header('Content-type: image/png'); - ImagePNG($im); - ImageDestroy($im); + imagepng($im); + imagedestroy($im); return true; } } return false; } + /** + * @param int $compressionid + * + * @return string + */ public function BMPcompressionWindowsLookup($compressionid) { static $BMPcompressionWindowsLookup = array( 0 => 'BI_RGB', @@ -674,6 +687,11 @@ class getid3_bmp extends getid3_handler return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid'); } + /** + * @param int $compressionid + * + * @return string + */ public function BMPcompressionOS2Lookup($compressionid) { static $BMPcompressionOS2Lookup = array( 0 => 'BI_RGB', diff --git a/lib/getid3/module.graphic.efax.php b/lib/getid3/module.graphic.efax.php index 8c6285432c99f06444acd0d21790ceca233e1ec8..426ef3e6f7c35d1b0f4c399c9a3130a3032cf804 100644 --- a/lib/getid3/module.graphic.efax.php +++ b/lib/getid3/module.graphic.efax.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.efax.php // @@ -17,7 +17,9 @@ class getid3_efax extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -42,8 +44,8 @@ class getid3_efax extends getid3_handler $info['efax']['header']['pages'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 198, 2)); $info['efax']['header']['data_bytes'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 202, 4)); -$this->error('eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; return true; } diff --git a/lib/getid3/module.graphic.gif.php b/lib/getid3/module.graphic.gif.php index ab7d62aea6d0682ce54dd7605cda9882f40d296a..4fb1247ad2395a70bc75b172841a6c00205601c4 100644 --- a/lib/getid3/module.graphic.gif.php +++ b/lib/getid3/module.graphic.gif.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.gif.php // @@ -14,10 +14,15 @@ // /// ///////////////////////////////////////////////////////////////// - +/** + * @link https://www.w3.org/Graphics/GIF/spec-gif89a.txt + * @link http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + */ class getid3_gif extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -78,96 +83,112 @@ class getid3_gif extends getid3_handler $info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64; } -// if ($info['gif']['header']['flags']['global_color_table']) { -// $GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']); -// $offset = 0; -// for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) { -// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); -// } -// } -// -// // Image Descriptor -// while (!feof($this->getid3->fp)) { -// $NextBlockTest = $this->fread(1); -// switch ($NextBlockTest) { -// -// case ',': // ',' - Image separator character -// -// $ImageDescriptorData = $NextBlockTest.$this->fread(9); -// $ImageDescriptor = array(); -// $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); -// $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); -// $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2)); -// $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2)); -// $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1)); -// $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80); -// $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40); -// $info['gif']['image_descriptor'][] = $ImageDescriptor; -// -// if ($ImageDescriptor['flags']['use_local_color_map']) { -// -// $this->warning('This version of getID3() cannot parse local color maps for GIFs'); -// return true; -// -// } -//echo 'Start of raster data: '.$this->ftell().'<BR>'; -// $RasterData = array(); -// $RasterData['code_size'] = getid3_lib::LittleEndian2Int($this->fread(1)); -// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int($this->fread(1)); -// $info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData; -// -// $CurrentCodeSize = $RasterData['code_size'] + 1; -// for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) { -// $DefaultDataLookupTable[$i] = chr($i); -// } -// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code -// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code -// -// -// $NextValue = $this->GetLSBits($CurrentCodeSize); -// echo 'Clear Code: '.$NextValue.'<BR>'; -// -// $NextValue = $this->GetLSBits($CurrentCodeSize); -// echo 'First Color: '.$NextValue.'<BR>'; -// -// $Prefix = $NextValue; -//$i = 0; -// while ($i++ < 20) { -// $NextValue = $this->GetLSBits($CurrentCodeSize); -// echo $NextValue.'<BR>'; -// } -//return true; -// break; -// -// case '!': -// // GIF Extension Block -// $ExtensionBlockData = $NextBlockTest.$this->fread(2); -// $ExtensionBlock = array(); -// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); -// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); -// $ExtensionBlock['data'] = $this->fread($ExtensionBlock['byte_length']); -// $info['gif']['extension_blocks'][] = $ExtensionBlock; -// break; -// -// case ';': -// $info['gif']['terminator_offset'] = $this->ftell() - 1; -// // GIF Terminator -// break; -// -// default: -// break; -// -// -// } -// } + if ($info['gif']['header']['flags']['global_color_table']) { + $GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']); + $offset = 0; + for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) { + $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); + $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); + $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); + $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); + } + } + + // Image Descriptor + $info['gif']['animation']['animated'] = false; + while (!feof($this->getid3->fp)) { + $NextBlockTest = $this->fread(1); + switch ($NextBlockTest) { + +/* + case ',': // ',' - Image separator character + $ImageDescriptorData = $NextBlockTest.$this->fread(9); + $ImageDescriptor = array(); + $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); + $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); + $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2)); + $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2)); + $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1)); + $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80); + $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40); + $info['gif']['image_descriptor'][] = $ImageDescriptor; + + if ($ImageDescriptor['flags']['use_local_color_map']) { + + $this->warning('This version of getID3() cannot parse local color maps for GIFs'); + return true; + + } + $RasterData = array(); + $RasterData['code_size'] = getid3_lib::LittleEndian2Int($this->fread(1)); + $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int($this->fread(1)); + $info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData; + + $CurrentCodeSize = $RasterData['code_size'] + 1; + for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) { + $DefaultDataLookupTable[$i] = chr($i); + } + $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code + $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code + + $NextValue = $this->GetLSBits($CurrentCodeSize); + echo 'Clear Code: '.$NextValue.'<BR>'; + + $NextValue = $this->GetLSBits($CurrentCodeSize); + echo 'First Color: '.$NextValue.'<BR>'; + + $Prefix = $NextValue; + $i = 0; + while ($i++ < 20) { + $NextValue = $this->GetLSBits($CurrentCodeSize); + echo $NextValue.'<br>'; + } + echo 'escaping<br>'; + return true; + break; +*/ + + case '!': + // GIF Extension Block + $ExtensionBlockData = $NextBlockTest.$this->fread(2); + $ExtensionBlock = array(); + $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); + $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); + $ExtensionBlock['data'] = (($ExtensionBlock['byte_length'] > 0) ? $this->fread($ExtensionBlock['byte_length']) : null); + + if (substr($ExtensionBlock['data'], 0, 11) == 'NETSCAPE2.0') { // Netscape Application Block (NAB) + $ExtensionBlock['data'] .= $this->fread(4); + if (substr($ExtensionBlock['data'], 11, 2) == "\x03\x01") { + $info['gif']['animation']['animated'] = true; + $info['gif']['animation']['loop_count'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlock['data'], 13, 2)); + } else { + $this->warning('Expecting 03 01 at offset '.($this->ftell() - 4).', found "'.getid3_lib::PrintHexBytes(substr($ExtensionBlock['data'], 11, 2)).'"'); + } + } + + $info['gif']['extension_blocks'][] = $ExtensionBlock; + break; + + case ';': + $info['gif']['terminator_offset'] = $this->ftell() - 1; + // GIF Terminator + break; + + default: + break; + + + } + } return true; } - + /** + * @param int $bits + * + * @return float|int + */ public function GetLSBits($bits) { static $bitbuffer = ''; while (strlen($bitbuffer) < $bits) { diff --git a/lib/getid3/module.graphic.jpg.php b/lib/getid3/module.graphic.jpg.php index 76edf145de6edf51fcf603497ef45927aa0579e9..3c874f32b47b2e09e657e412193369c581500cd1 100644 --- a/lib/getid3/module.graphic.jpg.php +++ b/lib/getid3/module.graphic.jpg.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.jpg.php // @@ -18,8 +18,9 @@ class getid3_jpg extends getid3_handler { - - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -33,7 +34,7 @@ class getid3_jpg extends getid3_handler $imageinfo = array(); //list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo); - list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474 + list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // https://www.getid3.org/phpBB3/viewtopic.php?t=1474 if (isset($imageinfo['APP13'])) { @@ -66,8 +67,8 @@ class getid3_jpg extends getid3_handler if (isset($imageinfo['APP1'])) { if (function_exists('exif_read_data')) { if (substr($imageinfo['APP1'], 0, 4) == 'Exif') { -//$this->warning('known issue: https://bugs.php.net/bug.php?id=62523'); -//return false; + //$this->warning('known issue: https://bugs.php.net/bug.php?id=62523'); + //return false; set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) { if (!(error_reporting() & $errno)) { // error is not specified in the error_reporting setting, so we ignore it @@ -108,6 +109,7 @@ class getid3_jpg extends getid3_handler if (isset($info['jpg']['exif']['GPS'])) { if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) { + $version_subparts = array(); for ($i = 0; $i < 4; $i++) { $version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1)); } @@ -131,6 +133,7 @@ class getid3_jpg extends getid3_handler if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) { $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1); + $computed_latitude = array(); foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) { $computed_latitude[$key] = getid3_lib::DecimalizeFraction($value); } @@ -139,6 +142,7 @@ class getid3_jpg extends getid3_handler if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) { $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1); + $computed_longitude = array(); foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) { $computed_longitude[$key] = getid3_lib::DecimalizeFraction($value); } @@ -176,7 +180,11 @@ class getid3_jpg extends getid3_handler return true; } - + /** + * @param mixed $value + * + * @return mixed + */ public function CastAsAppropriate($value) { if (is_array($value)) { return $value; @@ -190,7 +198,11 @@ class getid3_jpg extends getid3_handler return $value; } - + /** + * @param int $iptc_record + * + * @return string + */ public function IPTCrecordName($iptc_record) { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html static $IPTCrecordName = array(); @@ -207,7 +219,12 @@ class getid3_jpg extends getid3_handler return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : ''); } - + /** + * @param int $iptc_record + * @param int $iptc_tagkey + * + * @return string + */ public function IPTCrecordTagName($iptc_record, $iptc_tagkey) { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html static $IPTCrecordTagName = array(); diff --git a/lib/getid3/module.graphic.pcd.php b/lib/getid3/module.graphic.pcd.php index c1efaa21830f9d54fef3a294c5b17170543644db..882447965819934aafb052f7d2fc28237ca8f9c7 100644 --- a/lib/getid3/module.graphic.pcd.php +++ b/lib/getid3/module.graphic.pcd.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.pcd.php // @@ -19,6 +19,9 @@ class getid3_pcd extends getid3_handler { public $ExtractData = 0; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -73,11 +76,11 @@ class getid3_pcd extends getid3_handler for ($x = 0; $x < $PCD_width; $x++) { if ($PCDisVertical) { - $info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - $info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + $info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)})); + $info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)})); } else { - $info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - $info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + $info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)})); + $info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)})); } } } @@ -99,6 +102,13 @@ class getid3_pcd extends getid3_handler } + /** + * @param int $Y + * @param int $Cb + * @param int $Cr + * + * @return int + */ public function YCbCr2RGB($Y, $Cb, $Cr) { static $YCbCr_constants = array(); if (empty($YCbCr_constants)) { diff --git a/lib/getid3/module.graphic.png.php b/lib/getid3/module.graphic.png.php index c30caa43899ff1773ab1d329835de6be064e8d66..a9827f2d2086f223471631801a64be60c9fb61f2 100644 --- a/lib/getid3/module.graphic.png.php +++ b/lib/getid3/module.graphic.png.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.png.php // @@ -17,8 +17,17 @@ class getid3_png extends getid3_handler { - public $max_data_bytes = 10000000; // if data chunk is larger than this do not read it completely (getID3 only needs the first few dozen bytes for parsing) - + /** + * If data chunk is larger than this do not read it completely (getID3 only needs the first + * few dozen bytes for parsing). + * + * @var int + */ + public $max_data_bytes = 10000000; + + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -138,6 +147,7 @@ class getid3_png extends getid3_handler case 4: case 6: $this->error('Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']); + break; default: $this->warning('Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']); @@ -434,11 +444,46 @@ class getid3_png extends getid3_handler $thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk; break; - case 'IEND': // Image Trailer $thisfile_png_chunk_type_text['header'] = $chunk; break; + case 'acTL': // Animation Control chunk + // https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk + $thisfile_png['animation']['num_frames'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4)); // Number of frames + $thisfile_png['animation']['num_plays'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4)); // Number of times to loop this APNG. 0 indicates infinite looping. + + unset($chunk['data']); + $thisfile_png_chunk_type_text['header'] = $chunk; + break; + + case 'fcTL': // Frame Control chunk + // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk + $fcTL = array(); + $fcTL['sequence_number'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4)); // Sequence number of the animation chunk, starting from 0 + $fcTL['width'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4)); // Width of the following frame + $fcTL['height'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 4)); // Height of the following frame + $fcTL['x_offset'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 4)); // X position at which to render the following frame + $fcTL['y_offset'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 16, 4)); // Y position at which to render the following frame + $fcTL['delay_num'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 20, 2)); // Frame delay fraction numerator + $fcTL['delay_den'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 22, 2)); // Frame delay fraction numerator + $fcTL['dispose_op'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 23, 1)); // Type of frame area disposal to be done after rendering this frame + $fcTL['blend_op'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 23, 1)); // Type of frame area rendering for this frame + if ($fcTL['delay_den']) { + $fcTL['delay'] = $fcTL['delay_num'] / $fcTL['delay_den']; + } + $thisfile_png['animation']['fcTL'][$fcTL['sequence_number']] = $fcTL; + + unset($chunk['data']); + $thisfile_png_chunk_type_text['header'] = $chunk; + break; + + case 'fdAT': // Frame Data chunk + // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk + // "The `fdAT` chunk has the same purpose as an `IDAT` chunk. It has the same structure as an `IDAT` chunk, except preceded by a sequence number." + unset($chunk['data']); + $thisfile_png_chunk_type_text['header'] = $chunk; + break; default: //unset($chunk['data']); @@ -447,10 +492,21 @@ class getid3_png extends getid3_handler break; } } - + if (!empty($thisfile_png['animation']['num_frames']) && !empty($thisfile_png['animation']['fcTL'])) { + $info['video']['dataformat'] = 'apng'; + $info['playtime_seconds'] = 0; + foreach ($thisfile_png['animation']['fcTL'] as $seqno => $fcTL) { + $info['playtime_seconds'] += $fcTL['delay']; + } + } return true; } + /** + * @param int $sRGB + * + * @return string + */ public function PNGsRGBintentLookup($sRGB) { static $PNGsRGBintentLookup = array( 0 => 'Perceptual', @@ -461,6 +517,11 @@ class getid3_png extends getid3_handler return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid'); } + /** + * @param int $compressionmethod + * + * @return string + */ public function PNGcompressionMethodLookup($compressionmethod) { static $PNGcompressionMethodLookup = array( 0 => 'deflate/inflate' @@ -468,6 +529,11 @@ class getid3_png extends getid3_handler return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid'); } + /** + * @param int $unitid + * + * @return string + */ public function PNGpHYsUnitLookup($unitid) { static $PNGpHYsUnitLookup = array( 0 => 'unknown', @@ -476,6 +542,11 @@ class getid3_png extends getid3_handler return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid'); } + /** + * @param int $unitid + * + * @return string + */ public function PNGoFFsUnitLookup($unitid) { static $PNGoFFsUnitLookup = array( 0 => 'pixel', @@ -484,6 +555,11 @@ class getid3_png extends getid3_handler return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid'); } + /** + * @param int $equationtype + * + * @return string + */ public function PNGpCALequationTypeLookup($equationtype) { static $PNGpCALequationTypeLookup = array( 0 => 'Linear mapping', @@ -494,6 +570,11 @@ class getid3_png extends getid3_handler return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid'); } + /** + * @param int $unitid + * + * @return string + */ public function PNGsCALUnitLookup($unitid) { static $PNGsCALUnitLookup = array( 0 => 'meter', @@ -502,6 +583,12 @@ class getid3_png extends getid3_handler return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid'); } + /** + * @param int $color_type + * @param int $bit_depth + * + * @return int|false + */ public function IHDRcalculateBitsPerSample($color_type, $bit_depth) { switch ($color_type) { case 0: // Each pixel is a grayscale sample. diff --git a/lib/getid3/module.graphic.svg.php b/lib/getid3/module.graphic.svg.php index e4502aaeaee4ae26280182fcd085e680870a871a..73b9d843896b9ce23c67fd56259ba3d31fc27279 100644 --- a/lib/getid3/module.graphic.svg.php +++ b/lib/getid3/module.graphic.svg.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.graphic.svg.php // @@ -17,8 +17,9 @@ class getid3_svg extends getid3_handler { - - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.graphic.tiff.php b/lib/getid3/module.graphic.tiff.php index 90dcb5c855d25050dd80b86ee7406ea4bb02bd6f..57d500f55b97eb29757e532830097e18f7725cf7 100644 --- a/lib/getid3/module.graphic.tiff.php +++ b/lib/getid3/module.graphic.tiff.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.tiff.php // @@ -17,7 +17,9 @@ class getid3_tiff extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -60,6 +62,8 @@ class getid3_tiff extends getid3_handler $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4); + $CurrentIFD['fields'][$i]['raw']['tag_name'] = $this->TIFFcommentName($CurrentIFD['fields'][$i]['raw']['tag']); + switch ($CurrentIFD['fields'][$i]['raw']['type']) { case 1: // BYTE An 8-bit unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { @@ -165,7 +169,7 @@ class getid3_tiff extends getid3_handler case 306: // DateTime case 315: // Artist case 316: // HostComputer - $TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']); + $TIFFcommentName = strtolower($fieldarray['raw']['tag_name']); if (isset($info['tiff']['comments'][$TIFFcommentName])) { $info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; } else { @@ -182,7 +186,12 @@ class getid3_tiff extends getid3_handler return true; } - + /** + * @param string $bytestring + * @param string $byteorder + * + * @return int|float|false + */ public function TIFFendian2Int($bytestring, $byteorder) { if ($byteorder == 'Intel') { return getid3_lib::LittleEndian2Int($bytestring); @@ -192,6 +201,11 @@ class getid3_tiff extends getid3_handler return false; } + /** + * @param int $id + * + * @return string + */ public function TIFFcompressionMethod($id) { static $TIFFcompressionMethod = array(); if (empty($TIFFcompressionMethod)) { @@ -206,20 +220,252 @@ class getid3_tiff extends getid3_handler return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); } + /** + * @param int $id + * + * @return string + */ public function TIFFcommentName($id) { + // https://www.awaresystems.be/imaging/tiff/tifftags.html static $TIFFcommentName = array(); if (empty($TIFFcommentName)) { $TIFFcommentName = array( - 270 => 'imagedescription', - 271 => 'make', - 272 => 'model', - 305 => 'software', - 306 => 'datetime', - 315 => 'artist', - 316 => 'hostcomputer', + 254 => 'NewSubfileType', + 255 => 'SubfileType', + 256 => 'ImageWidth', + 257 => 'ImageLength', + 258 => 'BitsPerSample', + 259 => 'Compression', + 262 => 'PhotometricInterpretation', + 263 => 'Threshholding', + 264 => 'CellWidth', + 265 => 'CellLength', + 266 => 'FillOrder', + 269 => 'DocumentName', + 270 => 'ImageDescription', + 271 => 'Make', + 272 => 'Model', + 273 => 'StripOffsets', + 274 => 'Orientation', + 277 => 'SamplesPerPixel', + 278 => 'RowsPerStrip', + 279 => 'StripByteCounts', + 280 => 'MinSampleValue', + 281 => 'MaxSampleValue', + 282 => 'XResolution', + 283 => 'YResolution', + 284 => 'PlanarConfiguration', + 285 => 'PageName', + 286 => 'XPosition', + 287 => 'YPosition', + 288 => 'FreeOffsets', + 289 => 'FreeByteCounts', + 290 => 'GrayResponseUnit', + 291 => 'GrayResponseCurve', + 292 => 'T4Options', + 293 => 'T6Options', + 296 => 'ResolutionUnit', + 297 => 'PageNumber', + 301 => 'TransferFunction', + 305 => 'Software', + 306 => 'DateTime', + 315 => 'Artist', + 316 => 'HostComputer', + 317 => 'Predictor', + 318 => 'WhitePoint', + 319 => 'PrimaryChromaticities', + 320 => 'ColorMap', + 321 => 'HalftoneHints', + 322 => 'TileWidth', + 323 => 'TileLength', + 324 => 'TileOffsets', + 325 => 'TileByteCounts', + 326 => 'BadFaxLines', + 327 => 'CleanFaxData', + 328 => 'ConsecutiveBadFaxLines', + 330 => 'SubIFDs', + 332 => 'InkSet', + 333 => 'InkNames', + 334 => 'NumberOfInks', + 336 => 'DotRange', + 337 => 'TargetPrinter', + 338 => 'ExtraSamples', + 339 => 'SampleFormat', + 340 => 'SMinSampleValue', + 341 => 'SMaxSampleValue', + 342 => 'TransferRange', + 343 => 'ClipPath', + 344 => 'XClipPathUnits', + 345 => 'YClipPathUnits', + 346 => 'Indexed', + 347 => 'JPEGTables', + 351 => 'OPIProxy', + 400 => 'GlobalParametersIFD', + 401 => 'ProfileType', + 402 => 'FaxProfile', + 403 => 'CodingMethods', + 404 => 'VersionYear', + 405 => 'ModeNumber', + 433 => 'Decode', + 434 => 'DefaultImageColor', + 512 => 'JPEGProc', + 513 => 'JPEGInterchangeFormat', + 514 => 'JPEGInterchangeFormatLngth', + 515 => 'JPEGRestartInterval', + 517 => 'JPEGLosslessPredictors', + 518 => 'JPEGPointTransforms', + 519 => 'JPEGQTables', + 520 => 'JPEGDCTables', + 521 => 'JPEGACTables', + 529 => 'YCbCrCoefficients', + 530 => 'YCbCrSubSampling', + 531 => 'YCbCrPositioning', + 532 => 'ReferenceBlackWhite', + 559 => 'StripRowCounts', + 700 => 'XMP', + + 32781 => 'ImageID', + 33432 => 'Copyright', + 34732 => 'ImageLayer', + + // Private Tags - https://www.awaresystems.be/imaging/tiff/tifftags/private.html + 32932 => 'Wang Annotation', // Annotation data, as used in 'Imaging for Windows'. + 33445 => 'MD FileTag', // Specifies the pixel data format encoding in the Molecular Dynamics GEL file format. + 33446 => 'MD ScalePixel', // Specifies a scale factor in the Molecular Dynamics GEL file format. + 33447 => 'MD ColorTable', // Used to specify the conversion from 16bit to 8bit in the Molecular Dynamics GEL file format. + 33448 => 'MD LabName', // Name of the lab that scanned this file, as used in the Molecular Dynamics GEL file format. + 33449 => 'MD SampleInfo', // Information about the sample, as used in the Molecular Dynamics GEL file format. + 33450 => 'MD PrepDate', // Date the sample was prepared, as used in the Molecular Dynamics GEL file format. + 33451 => 'MD PrepTime', // Time the sample was prepared, as used in the Molecular Dynamics GEL file format. + 33452 => 'MD FileUnits', // Units for data in this file, as used in the Molecular Dynamics GEL file format. + 33550 => 'ModelPixelScaleTag', // Used in interchangeable GeoTIFF files. + 33723 => 'IPTC', // IPTC (International Press Telecommunications Council) metadata. + 33918 => 'INGR Packet Data Tag', // Intergraph Application specific storage. + 33919 => 'INGR Flag Registers', // Intergraph Application specific flags. + 33920 => 'IrasB Transformation Matrix', // Originally part of Intergraph's GeoTIFF tags, but likely understood by IrasB only. + 33922 => 'ModelTiepointTag', // Originally part of Intergraph's GeoTIFF tags, but now used in interchangeable GeoTIFF files. + 34264 => 'ModelTransformationTag', // Used in interchangeable GeoTIFF files. + 34377 => 'Photoshop', // Collection of Photoshop 'Image Resource Blocks'. + 34665 => 'Exif IFD', // A pointer to the Exif IFD. + 34675 => 'ICC Profile', // ICC profile data. + 34735 => 'GeoKeyDirectoryTag', // Used in interchangeable GeoTIFF files. + 34736 => 'GeoDoubleParamsTag', // Used in interchangeable GeoTIFF files. + 34737 => 'GeoAsciiParamsTag', // Used in interchangeable GeoTIFF files. + 34853 => 'GPS IFD', // A pointer to the Exif-related GPS Info IFD. + 34908 => 'HylaFAX FaxRecvParams', // Used by HylaFAX. + 34909 => 'HylaFAX FaxSubAddress', // Used by HylaFAX. + 34910 => 'HylaFAX FaxRecvTime', // Used by HylaFAX. + 37724 => 'ImageSourceData', // Used by Adobe Photoshop. + 40965 => 'Interoperability IFD', // A pointer to the Exif-related Interoperability IFD. + 42112 => 'GDAL_METADATA', // Used by the GDAL library, holds an XML list of name=value 'metadata' values about the image as a whole, and about specific samples. + 42113 => 'GDAL_NODATA', // Used by the GDAL library, contains an ASCII encoded nodata or background pixel value. + 50215 => 'Oce Scanjob Description', // Used in the Oce scanning process. + 50216 => 'Oce Application Selector', // Used in the Oce scanning process. + 50217 => 'Oce Identification Number', // Used in the Oce scanning process. + 50218 => 'Oce ImageLogic Characteristics', // Used in the Oce scanning process. + 50706 => 'DNGVersion', // Used in IFD 0 of DNG files. + 50707 => 'DNGBackwardVersion', // Used in IFD 0 of DNG files. + 50708 => 'UniqueCameraModel', // Used in IFD 0 of DNG files. + 50709 => 'LocalizedCameraModel', // Used in IFD 0 of DNG files. + 50710 => 'CFAPlaneColor', // Used in Raw IFD of DNG files. + 50711 => 'CFALayout', // Used in Raw IFD of DNG files. + 50712 => 'LinearizationTable', // Used in Raw IFD of DNG files. + 50713 => 'BlackLevelRepeatDim', // Used in Raw IFD of DNG files. + 50714 => 'BlackLevel', // Used in Raw IFD of DNG files. + 50715 => 'BlackLevelDeltaH', // Used in Raw IFD of DNG files. + 50716 => 'BlackLevelDeltaV', // Used in Raw IFD of DNG files. + 50717 => 'WhiteLevel', // Used in Raw IFD of DNG files. + 50718 => 'DefaultScale', // Used in Raw IFD of DNG files. + 50719 => 'DefaultCropOrigin', // Used in Raw IFD of DNG files. + 50720 => 'DefaultCropSize', // Used in Raw IFD of DNG files. + 50721 => 'ColorMatrix1', // Used in IFD 0 of DNG files. + 50722 => 'ColorMatrix2', // Used in IFD 0 of DNG files. + 50723 => 'CameraCalibration1', // Used in IFD 0 of DNG files. + 50724 => 'CameraCalibration2', // Used in IFD 0 of DNG files. + 50725 => 'ReductionMatrix1', // Used in IFD 0 of DNG files. + 50726 => 'ReductionMatrix2', // Used in IFD 0 of DNG files. + 50727 => 'AnalogBalance', // Used in IFD 0 of DNG files. + 50728 => 'AsShotNeutral', // Used in IFD 0 of DNG files. + 50729 => 'AsShotWhiteXY', // Used in IFD 0 of DNG files. + 50730 => 'BaselineExposure', // Used in IFD 0 of DNG files. + 50731 => 'BaselineNoise', // Used in IFD 0 of DNG files. + 50732 => 'BaselineSharpness', // Used in IFD 0 of DNG files. + 50733 => 'BayerGreenSplit', // Used in Raw IFD of DNG files. + 50734 => 'LinearResponseLimit', // Used in IFD 0 of DNG files. + 50735 => 'CameraSerialNumber', // Used in IFD 0 of DNG files. + 50736 => 'LensInfo', // Used in IFD 0 of DNG files. + 50737 => 'ChromaBlurRadius', // Used in Raw IFD of DNG files. + 50738 => 'AntiAliasStrength', // Used in Raw IFD of DNG files. + 50740 => 'DNGPrivateData', // Used in IFD 0 of DNG files. + 50741 => 'MakerNoteSafety', // Used in IFD 0 of DNG files. + 50778 => 'CalibrationIlluminant1', // Used in IFD 0 of DNG files. + 50779 => 'CalibrationIlluminant2', // Used in IFD 0 of DNG files. + 50780 => 'BestQualityScale', // Used in Raw IFD of DNG files. + 50784 => 'Alias Layer Metadata', // Alias Sketchbook Pro layer usage description. + 50908 => 'TIFF_RSID', // This private tag is used in a GEOTIFF standard by DGIWG. + 50909 => 'GEO_METADATA', // This private tag is used in a GEOTIFF standard by DGIWG. + + // EXIF tags - https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html + 33434 => 'ExposureTime', // Exposure time, given in seconds. + 33437 => 'FNumber', // The F number. + 34850 => 'ExposureProgram', // The class of the program used by the camera to set exposure when the picture is taken. + 34852 => 'SpectralSensitivity', // Indicates the spectral sensitivity of each channel of the camera used. + 34855 => 'ISOSpeedRatings', // Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232. + 34856 => 'OECF', // Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524. + 36864 => 'ExifVersion', // The version of the supported Exif standard. + 36867 => 'DateTimeOriginal', // The date and time when the original image data was generated. + 36868 => 'DateTimeDigitized', // The date and time when the image was stored as digital data. + 37121 => 'ComponentsConfiguration', // Specific to compressed data; specifies the channels and complements PhotometricInterpretation + 37122 => 'CompressedBitsPerPixel', // Specific to compressed data; states the compressed bits per pixel. + 37377 => 'ShutterSpeedValue', // Shutter speed. + 37378 => 'ApertureValue', // The lens aperture. + 37379 => 'BrightnessValue', // The value of brightness. + 37380 => 'ExposureBiasValue', // The exposure bias. + 37381 => 'MaxApertureValue', // The smallest F number of the lens. + 37382 => 'SubjectDistance', // The distance to the subject, given in meters. + 37383 => 'MeteringMode', // The metering mode. + 37384 => 'LightSource', // The kind of light source. + 37385 => 'Flash', // Indicates the status of flash when the image was shot. + 37386 => 'FocalLength', // The actual focal length of the lens, in mm. + 37396 => 'SubjectArea', // Indicates the location and area of the main subject in the overall scene. + 37500 => 'MakerNote', // Manufacturer specific information. + 37510 => 'UserComment', // Keywords or comments on the image; complements ImageDescription. + 37520 => 'SubsecTime', // A tag used to record fractions of seconds for the DateTime tag. + 37521 => 'SubsecTimeOriginal', // A tag used to record fractions of seconds for the DateTimeOriginal tag. + 37522 => 'SubsecTimeDigitized', // A tag used to record fractions of seconds for the DateTimeDigitized tag. + 40960 => 'FlashpixVersion', // The Flashpix format version supported by a FPXR file. + 40961 => 'ColorSpace', // The color space information tag is always recorded as the color space specifier. + 40962 => 'PixelXDimension', // Specific to compressed data; the valid width of the meaningful image. + 40963 => 'PixelYDimension', // Specific to compressed data; the valid height of the meaningful image. + 40964 => 'RelatedSoundFile', // Used to record the name of an audio file related to the image data. + 41483 => 'FlashEnergy', // Indicates the strobe energy at the time the image is captured, as measured in Beam Candle Power Seconds + 41484 => 'SpatialFrequencyResponse', // Records the camera or input device spatial frequency table and SFR values in the direction of image width, image height, and diagonal direction, as specified in ISO 12233. + 41486 => 'FocalPlaneXResolution', // Indicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane. + 41487 => 'FocalPlaneYResolution', // Indicates the number of pixels in the image height (Y) direction per FocalPlaneResolutionUnit on the camera focal plane. + 41488 => 'FocalPlaneResolutionUnit', // Indicates the unit for measuring FocalPlaneXResolution and FocalPlaneYResolution. + 41492 => 'SubjectLocation', // Indicates the location of the main subject in the scene. + 41493 => 'ExposureIndex', // Indicates the exposure index selected on the camera or input device at the time the image is captured. + 41495 => 'SensingMethod', // Indicates the image sensor type on the camera or input device. + 41728 => 'FileSource', // Indicates the image source. + 41729 => 'SceneType', // Indicates the type of scene. + 41730 => 'CFAPattern', // Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. + 41985 => 'CustomRendered', // Indicates the use of special processing on image data, such as rendering geared to output. + 41986 => 'ExposureMode', // Indicates the exposure mode set when the image was shot. + 41987 => 'WhiteBalance', // Indicates the white balance mode set when the image was shot. + 41988 => 'DigitalZoomRatio', // Indicates the digital zoom ratio when the image was shot. + 41989 => 'FocalLengthIn35mmFilm', // Indicates the equivalent focal length assuming a 35mm film camera, in mm. + 41990 => 'SceneCaptureType', // Indicates the type of scene that was shot. + 41991 => 'GainControl', // Indicates the degree of overall image gain adjustment. + 41992 => 'Contrast', // Indicates the direction of contrast processing applied by the camera when the image was shot. + 41993 => 'Saturation', // Indicates the direction of saturation processing applied by the camera when the image was shot. + 41994 => 'Sharpness', // Indicates the direction of sharpness processing applied by the camera when the image was shot. + 41995 => 'DeviceSettingDescription', // This tag indicates information on the picture-taking conditions of a particular camera model. + 41996 => 'SubjectDistanceRange', // Indicates the distance to the subject. + 42016 => 'ImageUniqueID', // Indicates an identifier assigned uniquely to each image. ); } return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')'); } + } diff --git a/lib/getid3/module.misc.cue.php b/lib/getid3/module.misc.cue.php index 9c42799e6ec65ec1438bb596e7e2b118f7676938..4602cdeca8bfa0e22bd7a64a1252ee90764d9387 100644 --- a/lib/getid3/module.misc.cue.php +++ b/lib/getid3/module.misc.cue.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.cue.php // @@ -35,6 +35,9 @@ class getid3_cue extends getid3_handler { public $cuesheet = array(); + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -44,18 +47,24 @@ class getid3_cue extends getid3_handler return true; } - - + /** + * @param string $filename + * + * @return array + */ public function readCueSheetFilename($filename) { $filedata = file_get_contents($filename); return $this->readCueSheet($filedata); } + /** - * Parses a cue sheet file. - * - * @param string $filename - The filename for the cue sheet to open. - */ + * Parses a cue sheet file. + * + * @param string $filedata + * + * @return array + */ public function readCueSheet(&$filedata) { $cue_lines = array(); @@ -72,10 +81,10 @@ class getid3_cue extends getid3_handler } /** - * Parses the cue sheet array. - * - * @param array $file - The cuesheet as an array of each line. - */ + * Parses the cue sheet array. + * + * @param array $file - The cuesheet as an array of each line. + */ public function parseCueSheet($file) { //-1 means still global, all others are track specific @@ -125,11 +134,11 @@ class getid3_cue extends getid3_handler } /** - * Parses the REM command. - * - * @param string $line - The line in the cue file that contains the TRACK command. - * @param integer $track_on - The track currently processing. - */ + * Parses the REM command. + * + * @param string $line - The line in the cue file that contains the TRACK command. + * @param integer $track_on - The track currently processing. + */ public function parseComment($line, $track_on) { $explodedline = explode(' ', $line, 3); @@ -148,11 +157,12 @@ class getid3_cue extends getid3_handler } /** - * Parses the FILE command. - * - * @param string $line - The line in the cue file that contains the FILE command. - * @return array - Array of FILENAME and TYPE of file.. - */ + * Parses the FILE command. + * + * @param string $line - The line in the cue file that contains the FILE command. + * + * @return array - Array of FILENAME and TYPE of file.. + */ public function parseFile($line) { $line = substr($line, strpos($line, ' ') + 1); @@ -168,11 +178,11 @@ class getid3_cue extends getid3_handler } /** - * Parses the FLAG command. - * - * @param string $line - The line in the cue file that contains the TRACK command. - * @param integer $track_on - The track currently processing. - */ + * Parses the FLAG command. + * + * @param string $line - The line in the cue file that contains the TRACK command. + * @param integer $track_on - The track currently processing. + */ public function parseFlags($line, $track_on) { if ($track_on != -1) @@ -206,11 +216,11 @@ class getid3_cue extends getid3_handler } /** - * Collect any unidentified data. - * - * @param string $line - The line in the cue file that contains the TRACK command. - * @param integer $track_on - The track currently processing. - */ + * Collect any unidentified data. + * + * @param string $line - The line in the cue file that contains the TRACK command. + * @param integer $track_on - The track currently processing. + */ public function parseGarbage($line, $track_on) { if ( strlen($line) > 0 ) @@ -227,15 +237,16 @@ class getid3_cue extends getid3_handler } /** - * Parses the INDEX command of a TRACK. - * - * @param string $line - The line in the cue file that contains the TRACK command. - * @param integer $track_on - The track currently processing. - */ + * Parses the INDEX command of a TRACK. + * + * @param string $line - The line in the cue file that contains the TRACK command. + * @param integer $track_on - The track currently processing. + */ public function parseIndex($line, $track_on) { $type = strtolower(substr($line, 0, strpos($line, ' '))); $line = substr($line, strpos($line, ' ') + 1); + $number = 0; if ($type == 'index') { @@ -261,6 +272,10 @@ class getid3_cue extends getid3_handler } } + /** + * @param string $line + * @param int $track_on + */ public function parseString($line, $track_on) { $category = strtolower(substr($line, 0, strpos($line, ' '))); @@ -292,11 +307,11 @@ class getid3_cue extends getid3_handler } /** - * Parses the TRACK command. - * - * @param string $line - The line in the cue file that contains the TRACK command. - * @param integer $track_on - The track currently processing. - */ + * Parses the TRACK command. + * + * @param string $line - The line in the cue file that contains the TRACK command. + * @param integer $track_on - The track currently processing. + */ public function parseTrack($line, $track_on) { $line = substr($line, strpos($line, ' ') + 1); diff --git a/lib/getid3/module.misc.exe.php b/lib/getid3/module.misc.exe.php index 4f04ad72a2af3aa0e136d3490d02123382baecd4..57bea05116d477e356f9871a93a0d449b41b82fd 100644 --- a/lib/getid3/module.misc.exe.php +++ b/lib/getid3/module.misc.exe.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.exe.php // @@ -17,7 +17,9 @@ class getid3_exe extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -51,8 +53,8 @@ class getid3_exe extends getid3_handler $info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16; $info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16; -$this->error('EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; } diff --git a/lib/getid3/module.misc.iso.php b/lib/getid3/module.misc.iso.php index 047728563921abc76c847bf6520e3c7a9f87a36b..231a32a23747fe67db378013e0d2599ea322757b 100644 --- a/lib/getid3/module.misc.iso.php +++ b/lib/getid3/module.misc.iso.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.iso.php // @@ -17,7 +17,9 @@ class getid3_iso extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -55,7 +57,11 @@ class getid3_iso extends getid3_handler return true; } - + /** + * @param string $ISOheader + * + * @return bool + */ public function ParsePrimaryVolumeDescriptor(&$ISOheader) { // ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!! // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field @@ -129,7 +135,11 @@ class getid3_iso extends getid3_handler return true; } - + /** + * @param string $ISOheader + * + * @return bool + */ public function ParseSupplementaryVolumeDescriptor(&$ISOheader) { // ISO integer values are stored Both-Endian format!! // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field @@ -208,7 +218,9 @@ class getid3_iso extends getid3_handler return true; } - + /** + * @return bool + */ public function ParsePathTable() { $info = &$this->getid3->info; if (!isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { @@ -267,7 +279,11 @@ class getid3_iso extends getid3_handler return true; } - + /** + * @param array $directorydata + * + * @return array + */ public function ParseDirectoryRecord($directorydata) { $info = &$this->getid3->info; if (isset($info['iso']['supplementary_volume_descriptor'])) { @@ -278,11 +294,14 @@ class getid3_iso extends getid3_handler $this->fseek($directorydata['location_bytes']); $DirectoryRecordData = $this->fread(1); + $DirectoryRecord = array(); while (ord($DirectoryRecordData{0}) > 33) { $DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1); + $ThisDirectoryRecord = array(); + $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); $ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4)); @@ -321,6 +340,11 @@ class getid3_iso extends getid3_handler return $DirectoryRecord; } + /** + * @param string $ISOfilename + * + * @return string + */ public function ISOstripFilenameVersion($ISOfilename) { // convert 'filename.ext;1' to 'filename.ext' if (!strstr($ISOfilename, ';')) { @@ -330,6 +354,11 @@ class getid3_iso extends getid3_handler } } + /** + * @param string $ISOtime + * + * @return int|false + */ public function ISOtimeText2UNIXtime($ISOtime) { $UNIXyear = (int) substr($ISOtime, 0, 4); @@ -345,6 +374,11 @@ class getid3_iso extends getid3_handler return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); } + /** + * @param string $ISOtime + * + * @return int + */ public function ISOtime2UNIXtime($ISOtime) { // Represented by seven bytes: // 1: Number of years since 1900 @@ -366,6 +400,11 @@ class getid3_iso extends getid3_handler return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); } + /** + * @param int $BinaryValue + * + * @return int + */ public function TwosCompliment2Decimal($BinaryValue) { // http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html // First check if the number is negative or positive by looking at the sign bit. diff --git a/lib/getid3/module.misc.msoffice.php b/lib/getid3/module.misc.msoffice.php index 099e6fc901506a2963da26267415e0db0ec3eff1..9a60aa08d21014028ba534e8e1badbde58d0daba 100644 --- a/lib/getid3/module.misc.msoffice.php +++ b/lib/getid3/module.misc.msoffice.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.msoffice.php // @@ -17,7 +17,9 @@ class getid3_msoffice extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -30,8 +32,8 @@ class getid3_msoffice extends getid3_handler } $info['fileformat'] = 'msoffice'; -$this->error('MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); -return false; + $this->error('MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); + return false; } diff --git a/lib/getid3/module.misc.par2.php b/lib/getid3/module.misc.par2.php index 2312bb8464ee914baeafa5099ff5af01f97beb1c..f5da19370215226aca4b45f6c20de57218c7387d 100644 --- a/lib/getid3/module.misc.par2.php +++ b/lib/getid3/module.misc.par2.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.par2.php // @@ -17,7 +17,9 @@ class getid3_par2 extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.misc.pdf.php b/lib/getid3/module.misc.pdf.php index e8d25cba800d40c3425be590c14fb7755d5ae27d..c6896a69f40518e4ba54e68ec47065dad1d399c3 100644 --- a/lib/getid3/module.misc.pdf.php +++ b/lib/getid3/module.misc.pdf.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.misc.pdf.php // @@ -17,7 +17,9 @@ class getid3_pdf extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; diff --git a/lib/getid3/module.tag.apetag.php b/lib/getid3/module.tag.apetag.php index 938625a855f82119c34549c96d0b60f2aa0ec647..37971a6b99d3b6775074a8b95fe9bbae36f24644 100644 --- a/lib/getid3/module.tag.apetag.php +++ b/lib/getid3/module.tag.apetag.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.tag.apetag.php // @@ -16,9 +16,21 @@ class getid3_apetag extends getid3_handler { - public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory + /** + * true: return full data for all attachments; + * false: return no data for all attachments; + * integer: return data for attachments <= than this; + * string: save as file to this directory. + * + * @var int|bool|string + */ + public $inline_attachments = true; + public $overrideendoffset = 0; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -286,7 +298,7 @@ class getid3_apetag extends getid3_handler } } elseif (is_string($this->inline_attachments)) { $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); - if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { + if (!is_dir($this->inline_attachments) || !getID3::is_writable($this->inline_attachments)) { // cannot write, skip $this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'); unset($thisfile_ape_items_current['data']); @@ -296,7 +308,7 @@ class getid3_apetag extends getid3_handler // if we get this far, must be OK if (is_string($this->inline_attachments)) { $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset']; - if (!file_exists($destination_filename) || is_writable($destination_filename)) { + if (!file_exists($destination_filename) || getID3::is_writable($destination_filename)) { file_put_contents($destination_filename, $thisfile_ape_items_current['data']); } else { $this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'); @@ -335,6 +347,11 @@ class getid3_apetag extends getid3_handler return true; } + /** + * @param string $APEheaderFooterData + * + * @return array|false + */ public function parseAPEheaderFooter($APEheaderFooterData) { // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html @@ -359,6 +376,11 @@ class getid3_apetag extends getid3_handler return $headerfooterinfo; } + /** + * @param int $rawflagint + * + * @return array + */ public function parseAPEtagFlags($rawflagint) { // "Note: APE Tags 1.0 do not use any of the APE Tag flags. // All are set to zero on creation and ignored on reading." @@ -374,6 +396,11 @@ class getid3_apetag extends getid3_handler return $flags; } + /** + * @param int $contenttypeid + * + * @return string + */ public function APEcontentTypeFlagLookup($contenttypeid) { static $APEcontentTypeFlagLookup = array( 0 => 'utf-8', @@ -384,6 +411,11 @@ class getid3_apetag extends getid3_handler return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); } + /** + * @param string $itemkey + * + * @return bool + */ public function APEtagItemIsUTF8Lookup($itemkey) { static $APEtagItemIsUTF8Lookup = array( 'title', diff --git a/lib/getid3/module.tag.id3v1.php b/lib/getid3/module.tag.id3v1.php index d160e9b44adcd2bc6ccf95120ba9813d5862e706..cafc3fbaf8b8b83c19d5fab42b792a90f710e01a 100644 --- a/lib/getid3/module.tag.id3v1.php +++ b/lib/getid3/module.tag.id3v1.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.tag.id3v1.php // @@ -17,7 +17,9 @@ class getid3_id3v1 extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -124,10 +126,20 @@ class getid3_id3v1 extends getid3_handler return true; } + /** + * @param string $str + * + * @return string + */ public static function cutfield($str) { return trim(substr($str, 0, strcspn($str, "\x00"))); } + /** + * @param bool $allowSCMPXextended + * + * @return string[] + */ public static function ArrayOfGenres($allowSCMPXextended=false) { static $GenreLookup = array( 0 => 'Blues', @@ -312,6 +324,12 @@ class getid3_id3v1 extends getid3_handler return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); } + /** + * @param string $genreid + * @param bool $allowSCMPXextended + * + * @return string|false + */ public static function LookupGenreName($genreid, $allowSCMPXextended=true) { switch ($genreid) { case 'RX': @@ -328,6 +346,12 @@ class getid3_id3v1 extends getid3_handler return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); } + /** + * @param string $genre + * @param bool $allowSCMPXextended + * + * @return string|false + */ public static function LookupGenreID($genre, $allowSCMPXextended=false) { $GenreLookup = self::ArrayOfGenres($allowSCMPXextended); $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); @@ -339,6 +363,11 @@ class getid3_id3v1 extends getid3_handler return false; } + /** + * @param string $OriginalGenre + * + * @return string|false + */ public static function StandardiseID3v1GenreName($OriginalGenre) { if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) { return self::LookupGenreName($GenreID); @@ -346,6 +375,17 @@ class getid3_id3v1 extends getid3_handler return $OriginalGenre; } + /** + * @param string $title + * @param string $artist + * @param string $album + * @param string $year + * @param int $genreid + * @param string $comment + * @param int|string $track + * + * @return string + */ public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { $ID3v1Tag = 'TAG'; $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); diff --git a/lib/getid3/module.tag.id3v2.php b/lib/getid3/module.tag.id3v2.php index 829f5ee2d5534f35e22f23e861ac4517944419ef..c51dedb8018bab93b1c570a93364a8489312af26 100644 --- a/lib/getid3/module.tag.id3v2.php +++ b/lib/getid3/module.tag.id3v2.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// /// // // module.tag.id3v2.php // @@ -20,6 +20,9 @@ class getid3_id3v2 extends getid3_handler { public $StartingOffset = 0; + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -266,6 +269,10 @@ class getid3_id3v2 extends getid3_handler } break; // skip rest of ID3v2 header } + $frame_header = null; + $frame_name = null; + $frame_size = null; + $frame_flags = null; if ($id3v2_majorversion == 2) { // Frame ID $xx xx xx (three characters) // Size $xx xx xx (24-bit integer) @@ -431,7 +438,7 @@ class getid3_id3v2 extends getid3_handler $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); } if ($thisfile_id3v2['majorversion_footer'] <= 4) { - $id3_flags = ord(substr($footer{5})); + $id3_flags = ord($footer{5}); $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); @@ -498,7 +505,11 @@ class getid3_id3v2 extends getid3_handler return true; } - + /** + * @param string $genrestring + * + * @return array + */ public function ParseID3v2GenreString($genrestring) { // Parse genres into arrays of genreName and genreID // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' @@ -530,7 +541,7 @@ class getid3_id3v2 extends getid3_handler foreach ($genre_elements as $element) { $element = trim($element); if ($element) { - if (preg_match('#^[0-9]{1,3}#', $element)) { + if (preg_match('#^[0-9]{1,3}$#', $element)) { $clean_genres[] = getid3_id3v1::LookupGenreName($element); } else { $clean_genres[] = str_replace('((', '(', $element); @@ -540,7 +551,11 @@ class getid3_id3v2 extends getid3_handler return $clean_genres; } - + /** + * @param array $parsedFrame + * + * @return bool + */ public function ParseID3v2Frame(&$parsedFrame) { // shortcuts @@ -774,10 +789,10 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - $parsedFrame['url'] = $frame_urldata; - $parsedFrame['description'] = $frame_description; + $parsedFrame['url'] = $frame_urldata; // always ISO-8859-1 + $parsedFrame['description'] = $frame_description; // according to the frame text encoding if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); } unset($parsedFrame['data']); @@ -789,9 +804,9 @@ class getid3_id3v2 extends getid3_handler // described in 4.3.2.> // URL <text string> - $parsedFrame['url'] = trim($parsedFrame['data']); + $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); } unset($parsedFrame['data']); @@ -813,7 +828,7 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); - // http://www.getid3.org/phpBB3/viewtopic.php?t=1369 + // https://www.getid3.org/phpBB3/viewtopic.php?t=1369 // "this tag typically contains null terminated strings, which are associated in pairs" // "there are users that use the tag incorrectly" $IPLS_parts = array(); @@ -933,6 +948,7 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); $parsedFrame['data'] = substr($parsedFrame['data'], 10); + $deviationbitstream = ''; while ($frame_offset < strlen($parsedFrame['data'])) { $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); } @@ -1416,9 +1432,9 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if ($id3v2_majorversion == 2) { - $parsedFrame['imagetype'] = $frame_imagetype; + $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null; } else { - $parsedFrame['mime'] = $frame_mimetype; + $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null; } $parsedFrame['picturetypeid'] = $frame_picturetype; $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); @@ -1430,7 +1446,7 @@ class getid3_id3v2 extends getid3_handler $imageinfo = array(); if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { - $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); + $parsedFrame['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); if ($imagechunkcheck[0]) { $parsedFrame['image_width'] = $imagechunkcheck[0]; } @@ -1446,6 +1462,7 @@ class getid3_id3v2 extends getid3_handler unset($parsedFrame['data']); break; } + $dir = ''; if ($this->getid3->option_save_attachments === true) { // great /* @@ -1459,7 +1476,7 @@ class getid3_id3v2 extends getid3_handler */ } elseif (is_string($this->getid3->option_save_attachments)) { $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); - if (!is_dir($dir) || !is_writable($dir)) { + if (!is_dir($dir) || !getID3::is_writable($dir)) { // cannot write, skip $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)'); unset($parsedFrame['data']); @@ -1469,7 +1486,7 @@ class getid3_id3v2 extends getid3_handler // if we get this far, must be OK if (is_string($this->getid3->option_save_attachments)) { $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; - if (!file_exists($destination_filename) || is_writable($destination_filename)) { + if (!file_exists($destination_filename) || getID3::is_writable($destination_filename)) { file_put_contents($destination_filename, $parsedFrame['data']); } else { $this->warning('attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'); @@ -2181,11 +2198,20 @@ class getid3_id3v2 extends getid3_handler return true; } - + /** + * @param string $data + * + * @return string + */ public function DeUnsynchronise($data) { return str_replace("\xFF\x00", "\xFF", $data); } + /** + * @param int $index + * + * @return string + */ public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( 0x00 => 'No more than 128 frames and 1 MB total tag size', @@ -2196,6 +2222,11 @@ class getid3_id3v2 extends getid3_handler return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public function LookupExtendedHeaderRestrictionsTextEncodings($index) { static $LookupExtendedHeaderRestrictionsTextEncodings = array( 0x00 => 'No restrictions', @@ -2204,6 +2235,11 @@ class getid3_id3v2 extends getid3_handler return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { static $LookupExtendedHeaderRestrictionsTextFieldSize = array( 0x00 => 'No restrictions', @@ -2214,6 +2250,11 @@ class getid3_id3v2 extends getid3_handler return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public function LookupExtendedHeaderRestrictionsImageEncoding($index) { static $LookupExtendedHeaderRestrictionsImageEncoding = array( 0x00 => 'No restrictions', @@ -2222,6 +2263,11 @@ class getid3_id3v2 extends getid3_handler return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { static $LookupExtendedHeaderRestrictionsImageSizeSize = array( 0x00 => 'No restrictions', @@ -2232,6 +2278,11 @@ class getid3_id3v2 extends getid3_handler return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); } + /** + * @param string $currencyid + * + * @return string + */ public function LookupCurrencyUnits($currencyid) { $begin = __LINE__; @@ -2428,7 +2479,11 @@ class getid3_id3v2 extends getid3_handler return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); } - + /** + * @param string $currencyid + * + * @return string + */ public function LookupCurrencyCountry($currencyid) { $begin = __LINE__; @@ -2624,8 +2679,12 @@ class getid3_id3v2 extends getid3_handler return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); } - - + /** + * @param string $languagecode + * @param bool $casesensitive + * + * @return string + */ public static function LanguageLookup($languagecode, $casesensitive=false) { if (!$casesensitive) { @@ -3081,7 +3140,11 @@ class getid3_id3v2 extends getid3_handler return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); } - + /** + * @param int $index + * + * @return string + */ public static function ETCOEventLookup($index) { if (($index >= 0x17) && ($index <= 0xDF)) { return 'reserved for future use'; @@ -3125,6 +3188,11 @@ class getid3_id3v2 extends getid3_handler return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public static function SYTLContentTypeLookup($index) { static $SYTLContentTypeLookup = array( 0x00 => 'other', @@ -3141,6 +3209,12 @@ class getid3_id3v2 extends getid3_handler return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); } + /** + * @param int $index + * @param bool $returnarray + * + * @return array|string + */ public static function APICPictureTypeLookup($index, $returnarray=false) { static $APICPictureTypeLookup = array( 0x00 => 'Other', @@ -3171,6 +3245,11 @@ class getid3_id3v2 extends getid3_handler return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public static function COMRReceivedAsLookup($index) { static $COMRReceivedAsLookup = array( 0x00 => 'Other', @@ -3187,6 +3266,11 @@ class getid3_id3v2 extends getid3_handler return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); } + /** + * @param int $index + * + * @return string + */ public static function RVA2ChannelTypeLookup($index) { static $RVA2ChannelTypeLookup = array( 0x00 => 'Other', @@ -3203,6 +3287,11 @@ class getid3_id3v2 extends getid3_handler return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); } + /** + * @param string $framename + * + * @return string + */ public static function FrameNameLongLookup($framename) { $begin = __LINE__; @@ -3354,7 +3443,7 @@ class getid3_id3v2 extends getid3_handler TYER Year UFI Unique file identifier UFID Unique file identifier - ULT Unsychronised lyric/text transcription + ULT Unsynchronised lyric/text transcription USER Terms of use USLT Unsynchronised lyric/text transcription WAF Official audio file webpage @@ -3386,7 +3475,11 @@ class getid3_id3v2 extends getid3_handler // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html } - + /** + * @param string $framename + * + * @return string + */ public static function FrameNameShortLookup($framename) { $begin = __LINE__; @@ -3538,7 +3631,7 @@ class getid3_id3v2 extends getid3_handler TYER year UFI unique_file_identifier UFID unique_file_identifier - ULT unsychronised_lyric + ULT unsynchronised_lyric USER terms_of_use USLT unsynchronised_lyric WAF url_file @@ -3566,6 +3659,11 @@ class getid3_id3v2 extends getid3_handler return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); } + /** + * @param string $encoding + * + * @return string + */ public static function TextEncodingTerminatorLookup($encoding) { // http://www.id3.org/id3v2.4.0-structure.txt // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: @@ -3579,6 +3677,11 @@ class getid3_id3v2 extends getid3_handler return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00"); } + /** + * @param int $encoding + * + * @return string + */ public static function TextEncodingNameLookup($encoding) { // http://www.id3.org/id3v2.4.0-structure.txt // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: @@ -3592,6 +3695,12 @@ class getid3_id3v2 extends getid3_handler return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); } + /** + * @param string $framename + * @param int $id3v2majorversion + * + * @return bool|int + */ public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { switch ($id3v2majorversion) { case 2: @@ -3606,6 +3715,13 @@ class getid3_id3v2 extends getid3_handler return false; } + /** + * @param string $numberstring + * @param bool $allowdecimal + * @param bool $allownegative + * + * @return bool + */ public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { for ($i = 0; $i < strlen($numberstring); $i++) { if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { @@ -3621,6 +3737,11 @@ class getid3_id3v2 extends getid3_handler return true; } + /** + * @param string $datestamp + * + * @return bool + */ public static function IsValidDateStampString($datestamp) { if (strlen($datestamp) != 8) { return false; @@ -3649,10 +3770,20 @@ class getid3_id3v2 extends getid3_handler return true; } + /** + * @param int $majorversion + * + * @return int + */ public static function ID3v2HeaderLength($majorversion) { return (($majorversion == 2) ? 6 : 10); } + /** + * @param string $frame_name + * + * @return string|false + */ public static function ID3v22iTunesBrokenFrameName($frame_name) { // iTunes (multiple versions) has been known to write ID3v2.3 style frames // but use ID3v2.2 frame names, right-padded using either [space] or [null] diff --git a/lib/getid3/module.tag.lyrics3.php b/lib/getid3/module.tag.lyrics3.php index 1645396b1251b793b9cb3594d5714f6f4f8f07b1..80e81658c7ba92ff2a6f1fd16d353f9e314111a8 100644 --- a/lib/getid3/module.tag.lyrics3.php +++ b/lib/getid3/module.tag.lyrics3.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// /// // // module.tag.lyrics3.php // @@ -17,7 +17,9 @@ class getid3_lyrics3 extends getid3_handler { - + /** + * @return bool + */ public function Analyze() { $info = &$this->getid3->info; @@ -61,7 +63,7 @@ class getid3_lyrics3 extends getid3_handler // Lyrics3v2, no ID3v1, no APE - $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3size = (int) strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' $lyrics3offset = $info['filesize'] - $lyrics3size; $lyrics3version = 2; @@ -96,7 +98,7 @@ class getid3_lyrics3 extends getid3_handler } - if (isset($lyrics3offset)) { + if (isset($lyrics3offset) && isset($lyrics3version) && isset($lyrics3size)) { $info['avdataend'] = $lyrics3offset; $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); @@ -126,6 +128,13 @@ class getid3_lyrics3 extends getid3_handler return true; } + /** + * @param int $endoffset + * @param int $version + * @param int $length + * + * @return bool + */ public function getLyrics3Data($endoffset, $version, $length) { // http://www.volweb.cz/str/tags.htm @@ -142,6 +151,8 @@ class getid3_lyrics3 extends getid3_handler } $rawdata = $this->fread($length); + $ParsedLyrics3 = array(); + $ParsedLyrics3['raw']['lyrics3version'] = $version; $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; $ParsedLyrics3['tag_offset_start'] = $endoffset; @@ -250,6 +261,11 @@ class getid3_lyrics3 extends getid3_handler return true; } + /** + * @param string $rawtimestamp + * + * @return int|false + */ public function Lyrics3Timestamp2Seconds($rawtimestamp) { if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) { return (int) (($regs[1] * 60) + $regs[2]); @@ -257,8 +273,14 @@ class getid3_lyrics3 extends getid3_handler return false; } + /** + * @param array $Lyrics3data + * + * @return bool + */ public function Lyrics3LyricsTimestampParse(&$Lyrics3data) { $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); + $notimestamplyricsarray = array(); foreach ($lyricsarray as $key => $lyricline) { $regs = array(); unset($thislinetimestamps); @@ -287,6 +309,11 @@ class getid3_lyrics3 extends getid3_handler return true; } + /** + * @param string $char + * + * @return bool|null + */ public function IntString2Bool($char) { if ($char == '1') { return true; diff --git a/lib/getid3/module.tag.xmp.php b/lib/getid3/module.tag.xmp.php index 40dd8bd855b72c34f23e3504d31eacf5c4c01383..a976ef8eaa2cf85479c8ffb066630996137bf1f1 100644 --- a/lib/getid3/module.tag.xmp.php +++ b/lib/getid3/module.tag.xmp.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.tag.xmp.php // @@ -81,8 +81,8 @@ class Image_XMP * Reads all the JPEG header segments from an JPEG image file into an array * * @param string $filename - the filename of the JPEG file to read - * @return array $headerdata - Array of JPEG header segments - * @return boolean FALSE - if headers could not be read + * @return array|boolean $headerdata - Array of JPEG header segments, + * FALSE - if headers could not be read */ public function _get_jpeg_header_data($filename) { @@ -124,6 +124,7 @@ class Image_XMP // Flag that we havent yet hit the compressed image data $hit_compressed_image_data = false; + $headerdata = array(); // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, // 2) we have hit the compressed image data (no more headers are allowed after data) // 3) or end of file is hit @@ -191,8 +192,8 @@ class Image_XMP * Retrieves XMP information from an APP1 JPEG segment and returns the raw XML text as a string. * * @param string $filename - the filename of the JPEG file to read - * @return string $xmp_data - the string of raw XML text - * @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured + * @return string|boolean $xmp_data - the string of raw XML text, + * FALSE - if an APP 1 XMP segment could not be found, or if an error occured */ public function _get_XMP_text($filename) { @@ -212,7 +213,7 @@ class Image_XMP // Return the XMP text $xmp_data = substr($jpeg_header_data[$i]['SegData'], 29); - return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153) + return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see https://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153) } } } @@ -223,9 +224,9 @@ class Image_XMP * Parses a string containing XMP data (XML), and returns an array * which contains all the XMP (XML) information. * - * @param string $xml_text - a string containing the XMP data (XML) to be parsed - * @return array $xmp_array - an array containing all xmp details retrieved. - * @return boolean FALSE - couldn't parse the XMP data + * @param string $xmltext - a string containing the XMP data (XML) to be parsed + * @return array|boolean $xmp_array - an array containing all xmp details retrieved, + * FALSE - couldn't parse the XMP data. */ public function read_XMP_array_from_text($xmltext) { @@ -311,10 +312,12 @@ class Image_XMP } } } + break; case 'cdata': case 'close': break; } + break; case 'rdf:ID': case 'rdf:nodeID': @@ -397,7 +400,7 @@ class Image_XMP /** * Constructor * - * @param string - Name of the image file to access and extract XMP information from. + * @param string $sFilename - Name of the image file to access and extract XMP information from. */ public function __construct($sFilename) { @@ -409,8 +412,11 @@ class Image_XMP $xmp_data = $this->_get_XMP_text($sFilename); if ($xmp_data) { - $this->_aXMP = $this->read_XMP_array_from_text($xmp_data); - $this->_bXMPParse = true; + $aXMP = $this->read_XMP_array_from_text($xmp_data); + if ($aXMP !== false) { + $this->_aXMP = (array) $aXMP; + $this->_bXMPParse = true; + } } } } diff --git a/lib/getid3/write.apetag.php b/lib/getid3/write.apetag.php index d6044b29ac0cadf4e77e7cf21647c9112253ca8b..7fc507c22bc64dab2c5739abc12b3aa9473bedd8 100644 --- a/lib/getid3/write.apetag.php +++ b/lib/getid3/write.apetag.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.apetag.php // @@ -19,17 +19,43 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE class getid3_write_apetag { - + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data; - public $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * ReplayGain / MP3gain tags will be copied from old tag even if not passed in data. + * + * @var bool + */ + public $always_preserve_replaygain = true; + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteAPEtag() { // NOTE: All data passed to this function must be UTF-8 format @@ -57,7 +83,7 @@ class getid3_write_apetag } if ($APEtag = $this->GenerateAPEtag()) { - if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { $oldignoreuserabort = ignore_user_abort(true); flock($fp, LOCK_EX); @@ -92,11 +118,14 @@ class getid3_write_apetag return false; } + /** + * @return bool + */ public function DeleteAPEtag() { $getID3 = new getID3; $ThisFileInfo = $getID3->analyze($this->filename); if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { - if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { flock($fp, LOCK_EX); $oldignoreuserabort = ignore_user_abort(true); @@ -125,7 +154,9 @@ class getid3_write_apetag return true; } - + /** + * @return string|false + */ public function GenerateAPEtag() { // NOTE: All data passed to this function must be UTF-8 format @@ -160,6 +191,12 @@ class getid3_write_apetag return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false); } + /** + * @param array $items + * @param bool $isheader + * + * @return string + */ public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) { $tagdatalength = 0; foreach ($items as $itemdata) { @@ -176,6 +213,15 @@ class getid3_write_apetag return $APEheader; } + /** + * @param bool $header + * @param bool $footer + * @param bool $isheader + * @param int $encodingid + * @param bool $readonly + * + * @return string + */ public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) { $APEtagFlags = array_fill(0, 4, 0); if ($header) { @@ -201,6 +247,11 @@ class getid3_write_apetag return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]); } + /** + * @param string $itemkey + * + * @return string + */ public function CleanAPEtagItemKey($itemkey) { $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey); diff --git a/lib/getid3/write.id3v1.php b/lib/getid3/write.id3v1.php index 4fa6a6c37606cb90d926bde69072ab6cd77c3a57..6f84867d7469f90635557f5bbc8de639c37f533f 100644 --- a/lib/getid3/write.id3v1.php +++ b/lib/getid3/write.id3v1.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.id3v1.php // @@ -18,19 +18,44 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE_ class getid3_write_id3v1 { + /** + * @var string + */ public $filename; + + /** + * @var int + */ public $filesize; + + /** + * @var array + */ public $tag_data; - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteID3v1() { // File MUST be writeable - CHMOD(646) at least - if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { + if (!empty($this->filename) && is_readable($this->filename) && getID3::is_writable($this->filename) && is_file($this->filename)) { $this->setRealFileSize(); if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) { $this->errors[] = 'Unable to WriteID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; @@ -66,6 +91,9 @@ class getid3_write_id3v1 return false; } + /** + * @return bool + */ public function FixID3v1Padding() { // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces // This function rewrites the ID3v1 tag with correct padding @@ -79,6 +107,7 @@ class getid3_write_id3v1 $getID3->option_tag_id3v1 = true; $ThisFileInfo = $getID3->analyze($this->filename); if (isset($ThisFileInfo['tags']['id3v1'])) { + $id3v1data = array(); foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) { $id3v1data[$key] = implode(',', $value); } @@ -88,9 +117,12 @@ class getid3_write_id3v1 return false; } + /** + * @return bool + */ public function RemoveID3v1() { // File MUST be writeable - CHMOD(646) at least - if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { + if (!empty($this->filename) && is_readable($this->filename) && getID3::is_writable($this->filename) && is_file($this->filename)) { $this->setRealFileSize(); if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) { $this->errors[] = 'Unable to RemoveID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; @@ -116,6 +148,9 @@ class getid3_write_id3v1 return false; } + /** + * @return bool + */ public function setRealFileSize() { if (PHP_INT_MAX > 2147483647) { $this->filesize = filesize($this->filename); diff --git a/lib/getid3/write.id3v2.php b/lib/getid3/write.id3v2.php index 17138db8b9f0d398b1a3d5498a327f8aec30517e..fae45d29cd21060161f8fef5cb27d2c30bde3cfc 100644 --- a/lib/getid3/write.id3v2.php +++ b/lib/getid3/write.id3v2.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// /// // // write.id3v2.php // @@ -18,33 +18,96 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE_ class getid3_write_id3v2 { + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data; - public $fread_buffer_size = 32768; // read buffer size in bytes - public $paddedlength = 4096; // minimum length of ID3v2 tag in bytes - public $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4) - public $minorversion = 0; // ID3v2 minor version - always 0 - public $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags - public $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed - public $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it. - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * Read buffer size in bytes. + * + * @var int + */ + public $fread_buffer_size = 32768; + + /** + * Minimum length of ID3v2 tag in bytes. + * + * @var int + */ + public $paddedlength = 4096; + + /** + * ID3v2 major version (2, 3 (recommended), 4). + * + * @var int + */ + public $majorversion = 3; + + /** + * ID3v2 minor version - always 0. + * + * @var int + */ + public $minorversion = 0; + + /** + * If true, merge new data with existing tags; if false, delete old tag data and only write new tags. + * + * @var bool + */ + public $merge_existing_data = false; + + /** + * Default text encoding (ISO-8859-1) if not explicitly passed. + * + * @var int + */ + public $id3v2_default_encodingid = 0; + + /** + * The specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, + * so by default don't use it. + * + * @var bool + */ + public $id3v2_use_unsynchronisation = false; + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteID3v2() { // File MUST be writeable - CHMOD(646) at least. It's best if the // directory is also writeable, because that method is both faster and less susceptible to errors. - if (!empty($this->filename) && (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename))))) { + if (!empty($this->filename) && (getID3::is_writable($this->filename) || (!file_exists($this->filename) && getID3::is_writable(dirname($this->filename))))) { // Initialize getID3 engine $getID3 = new getID3; $OldThisFileInfo = $getID3->analyze($this->filename); if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) { $this->errors[] = 'Unable to write ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; - fclose($fp_source); return false; } if ($this->merge_existing_data) { @@ -57,12 +120,12 @@ class getid3_write_id3v2 if ($NewID3v2Tag = $this->GenerateID3v2Tag()) { - if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) { + if (file_exists($this->filename) && getID3::is_writable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) { // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary) if (file_exists($this->filename)) { - if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'r+b'))) { + if (is_readable($this->filename) && getID3::is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'r+b'))) { rewind($fp); fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); fclose($fp); @@ -72,7 +135,7 @@ class getid3_write_id3v2 } else { - if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'wb'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'wb'))) { rewind($fp); fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); fclose($fp); @@ -86,7 +149,7 @@ class getid3_write_id3v2 if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) { - if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { + if (getID3::is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag)); @@ -134,10 +197,13 @@ class getid3_write_id3v2 return false; } + /** + * @return bool + */ public function RemoveID3v2() { // File MUST be writeable - CHMOD(646) at least. It's best if the // directory is also writeable, because that method is both faster and less susceptible to errors. - if (is_writeable(dirname($this->filename))) { + if (getID3::is_writable(dirname($this->filename))) { // preferred method - only one copying operation, minimal chance of corrupting // original file if script is interrupted, but required directory to be writeable @@ -155,7 +221,7 @@ class getid3_write_id3v2 if ($OldThisFileInfo['avdataoffset'] !== false) { fseek($fp_source, $OldThisFileInfo['avdataoffset']); } - if (is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b'))) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) { fwrite($fp_temp, $buffer, strlen($buffer)); } @@ -172,7 +238,7 @@ class getid3_write_id3v2 } rename($this->filename.'getid3tmp', $this->filename); - } elseif (is_writable($this->filename)) { + } elseif (getID3::is_writable($this->filename)) { // less desirable alternate method - double-copies the file, overwrites original file // and could corrupt source file if the script is interrupted or an error occurs. @@ -195,7 +261,7 @@ class getid3_write_id3v2 fwrite($fp_temp, $buffer, strlen($buffer)); } fclose($fp_source); - if (is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'wb'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'wb'))) { rewind($fp_temp); while ($buffer = fread($fp_temp, $this->fread_buffer_size)) { fwrite($fp_source, $buffer, strlen($buffer)); @@ -225,8 +291,13 @@ class getid3_write_id3v2 return true; } - + /** + * @param array $flags + * + * @return string|false + */ public function GenerateID3v2TagFlags($flags) { + $flag = null; switch ($this->majorversion) { case 4: // %abcd0000 @@ -259,9 +330,22 @@ class getid3_write_id3v2 return chr(bindec($flag)); } - + /** + * @param bool $TagAlter + * @param bool $FileAlter + * @param bool $ReadOnly + * @param bool $Compression + * @param bool $Encryption + * @param bool $GroupingIdentity + * @param bool $Unsynchronisation + * @param bool $DataLengthIndicator + * + * @return string|false + */ public function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) { - switch ($this->majorversion) { + $flag1 = null; + $flag2 = null; + switch ($this->majorversion) { case 4: // %0abc0000 %0h00kmnp $flag1 = '0'; @@ -300,6 +384,12 @@ class getid3_write_id3v2 return chr(bindec($flag1)).chr(bindec($flag2)); } + /** + * @param string $frame_name + * @param array $source_data_array + * + * @return string|false + */ public function GenerateID3v2FrameData($frame_name, $source_data_array) { if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { return false; @@ -331,7 +421,7 @@ class getid3_write_id3v2 // Description <text string according to encoding> $00 (00) // Value <text string according to encoding> $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; } else { $framedata .= chr($source_data_array['encodingid']); @@ -346,9 +436,9 @@ class getid3_write_id3v2 // Description <text string according to encoding> $00 (00) // URL <text string> $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) { + } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false)) { //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; // probably should be an error, need to rewrite IsValidURL() to handle other encodings $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; @@ -364,7 +454,7 @@ class getid3_write_id3v2 // Text encoding $xx // People list strings <textstrings> $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; } else { $framedata .= chr($source_data_array['encodingid']); @@ -397,13 +487,14 @@ class getid3_write_id3v2 if (!$this->ID3v2IsValidETCOevent($val['typeid'])) { $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')'; } elseif (($key != 'timestampformat') && ($key != 'flags')) { - if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) { + if (($val['timestamp'] > 0) && isset($previousETCOtimestamp) && ($previousETCOtimestamp >= $val['timestamp'])) { // The 'Time stamp' is set to zero if directly at the beginning of the sound // or after the previous event. All events MUST be sorted in chronological order. $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')'; } else { $framedata .= chr($val['typeid']); $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); + $previousETCOtimestamp = $val['timestamp']; } } } @@ -453,6 +544,7 @@ class getid3_write_id3v2 } else { $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')'; } + $unwrittenbitstream = ''; foreach ($source_data_array as $key => $val) { if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) { $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT); @@ -617,7 +709,7 @@ class getid3_write_id3v2 if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) { $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)'; } else { - $incdecflag .= '00'; + $incdecflag = '00'; $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back @@ -762,7 +854,7 @@ class getid3_write_id3v2 $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion; } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) { $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion; - } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) { + } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false))) { //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; // probably should be an error, need to rewrite IsValidURL() to handle other encodings $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; @@ -835,7 +927,7 @@ class getid3_write_id3v2 $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name; } else { $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false); - $flag .= '0000000'; + $flag = '0000000'; $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0'; $framedata .= chr(bindec($flag)); $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false); @@ -867,7 +959,7 @@ class getid3_write_id3v2 // ID and additional data <text string(s)> if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) { $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')'; - } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) { + } elseif (!$this->IsValidURL($source_data_array['data'], true)) { //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; // probably should be an error, need to rewrite IsValidURL() to handle other encodings $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; @@ -914,6 +1006,7 @@ class getid3_write_id3v2 } else { $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')'; } + break; default: if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) { @@ -966,9 +1059,9 @@ class getid3_write_id3v2 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; - } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) { + } elseif (!getid3_id3v2::IsANumber($source_data_array['pricepaid']['value'], false)) { $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')'; - } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) { + } elseif (!getid3_id3v2::IsValidDateStampString($source_data_array['purchasedate'])) { $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)'; } else { $framedata .= chr($source_data_array['encodingid']); @@ -992,9 +1085,9 @@ class getid3_write_id3v2 $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; - } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) { + } elseif (!getid3_id3v2::IsValidDateStampString($source_data_array['pricevaliduntil'])) { $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)'; - } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) { + } elseif (!$this->IsValidURL($source_data_array['contacturl'], false)) { $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)'; } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) { $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)'; @@ -1003,6 +1096,7 @@ class getid3_write_id3v2 } else { $framedata .= chr($source_data_array['encodingid']); unset($pricestring); + $pricestrings = array(); foreach ($source_data_array['price'] as $key => $val) { if ($this->ID3v2IsValidPriceString($key.$val['value'])) { $pricestrings[] = $key.$val['value']; @@ -1155,7 +1249,7 @@ class getid3_write_id3v2 } elseif ($frame_name{0} == 'W') { // 4.3. W??? URL link frames // URL <text string> - if (!$this->IsValidURL($source_data_array['data'], false, false)) { + if (!$this->IsValidURL($source_data_array['data'], false)) { //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; // probably should be an error, need to rewrite IsValidURL() to handle other encodings $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; @@ -1174,6 +1268,12 @@ class getid3_write_id3v2 return $framedata; } + /** + * @param string|null $frame_name + * @param array $source_data_array + * + * @return bool + */ public function ID3v2FrameIsAllowed($frame_name, $source_data_array) { static $PreviousFrames = array(); @@ -1530,6 +1630,11 @@ class getid3_write_id3v2 return true; } + /** + * @param bool $noerrorsonly + * + * @return string|false + */ public function GenerateID3v2Tag($noerrorsonly=true) { $this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag() @@ -1644,20 +1749,31 @@ class getid3_write_id3v2 return false; } + /** + * @param string $pricestring + * + * @return bool + */ public function ID3v2IsValidPriceString($pricestring) { if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') { return false; - } elseif (!$this->IsANumber(substr($pricestring, 3), true)) { + } elseif (!getid3_id3v2::IsANumber(substr($pricestring, 3), true)) { return false; } return true; } + /** + * @param string $framename + * + * @return bool + */ public function ID3v2FrameFlagsLookupTagAlter($framename) { // unfinished switch ($framename) { case 'RGAD': $allow = true; + break; default: $allow = false; break; @@ -1665,6 +1781,11 @@ class getid3_write_id3v2 return $allow; } + /** + * @param string $framename + * + * @return bool + */ public function ID3v2FrameFlagsLookupFileAlter($framename) { // unfinished switch ($framename) { @@ -1678,6 +1799,11 @@ class getid3_write_id3v2 } } + /** + * @param int $eventid + * + * @return bool + */ public function ID3v2IsValidETCOevent($eventid) { if (($eventid < 0) || ($eventid > 0xFF)) { // outside range of 1 byte @@ -1698,6 +1824,11 @@ class getid3_write_id3v2 return true; } + /** + * @param int $contenttype + * + * @return bool + */ public function ID3v2IsValidSYLTtype($contenttype) { if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) { return true; @@ -1707,6 +1838,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $channeltype + * + * @return bool + */ public function ID3v2IsValidRVA2channeltype($channeltype) { if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) { return true; @@ -1714,6 +1850,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $picturetype + * + * @return bool + */ public function ID3v2IsValidAPICpicturetype($picturetype) { if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) { return true; @@ -1721,6 +1862,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int|string $imageformat + * + * @return bool + */ public function ID3v2IsValidAPICimageformat($imageformat) { if ($imageformat == '-->') { return true; @@ -1736,6 +1882,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $receivedas + * + * @return bool + */ public function ID3v2IsValidCOMRreceivedAs($receivedas) { if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) { return true; @@ -1743,6 +1894,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $RGADname + * + * @return bool + */ public static function ID3v2IsValidRGADname($RGADname) { if (($RGADname >= 0) && ($RGADname <= 2)) { return true; @@ -1750,6 +1906,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $RGADoriginator + * + * @return bool + */ public static function ID3v2IsValidRGADoriginator($RGADoriginator) { if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) { return true; @@ -1757,6 +1918,11 @@ class getid3_write_id3v2 return false; } + /** + * @param int $textencodingbyte + * + * @return bool + */ public function ID3v2IsValidTextEncoding($textencodingbyte) { // 0 = ISO-8859-1 // 1 = UTF-16 with BOM @@ -1770,6 +1936,11 @@ class getid3_write_id3v2 return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]); } + /** + * @param string $data + * + * @return string + */ public static function Unsynchronise($data) { // Whenever a false synchronisation is found within the tag, one zeroed // byte is inserted after the first false synchronisation byte. The @@ -1800,6 +1971,11 @@ class getid3_write_id3v2 return $unsyncheddata; } + /** + * @param mixed $var + * + * @return bool + */ public function is_hash($var) { // written by dev-nullØchristophe*vg // taken from http://www.php.net/manual/en/function.array-merge-recursive.php @@ -1815,6 +1991,12 @@ class getid3_write_id3v2 return false; } + /** + * @param mixed $arr1 + * @param mixed $arr2 + * + * @return array + */ public function array_join_merge($arr1, $arr2) { // written by dev-nullØchristophe*vg // taken from http://www.php.net/manual/en/function.array-merge-recursive.php @@ -1839,10 +2021,22 @@ class getid3_write_id3v2 } } + /** + * @param string $mimestring + * + * @return false|int + */ public static function IsValidMIMEstring($mimestring) { return preg_match('#^.+/.+$#', $mimestring); } + /** + * @param int $number + * @param int $maxbits + * @param bool $signed + * + * @return bool + */ public static function IsWithinBitRange($number, $maxbits, $signed=false) { if ($signed) { if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) { @@ -1856,6 +2050,11 @@ class getid3_write_id3v2 return false; } + /** + * @param string $email + * + * @return false|int|mixed + */ public static function IsValidEmail($email) { if (function_exists('filter_var')) { return filter_var($email, FILTER_VALIDATE_EMAIL); @@ -1864,6 +2063,12 @@ class getid3_write_id3v2 return preg_match('#^[^ ]+@[a-z\\-\\.]+\\.[a-z]{2,}$#', $email); } + /** + * @param string $url + * @param bool $allowUserPass + * + * @return bool + */ public static function IsValidURL($url, $allowUserPass=false) { if ($url == '') { return false; @@ -1876,7 +2081,7 @@ class getid3_write_id3v2 } } // 2016-06-08: relax URL checking to avoid falsely rejecting valid URLs, leave URL validation to the user - // http://www.getid3.org/phpBB3/viewtopic.php?t=1926 + // https://www.getid3.org/phpBB3/viewtopic.php?t=1926 return true; /* if ($parts = $this->safe_parse_url($url)) { @@ -1900,6 +2105,11 @@ class getid3_write_id3v2 */ } + /** + * @param string $url + * + * @return array + */ public static function safe_parse_url($url) { $parts = @parse_url($url); $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); @@ -1911,6 +2121,12 @@ class getid3_write_id3v2 return $parts; } + /** + * @param int $majorversion + * @param string $long_description + * + * @return string + */ public static function ID3v2ShortFrameNameLookup($majorversion, $long_description) { $long_description = str_replace(' ', '_', strtolower(trim($long_description))); static $ID3v2ShortFrameNameLookup = array(); @@ -1979,7 +2195,7 @@ class getid3_write_id3v2 $ID3v2ShortFrameNameLookup[2]['text'] = 'TXX'; $ID3v2ShortFrameNameLookup[2]['year'] = 'TYE'; $ID3v2ShortFrameNameLookup[2]['unique_file_identifier'] = 'UFI'; - $ID3v2ShortFrameNameLookup[2]['unsychronised_lyric'] = 'ULT'; + $ID3v2ShortFrameNameLookup[2]['unsynchronised_lyric'] = 'ULT'; $ID3v2ShortFrameNameLookup[2]['url_file'] = 'WAF'; $ID3v2ShortFrameNameLookup[2]['url_artist'] = 'WAR'; $ID3v2ShortFrameNameLookup[2]['url_source'] = 'WAS'; @@ -2054,7 +2270,7 @@ class getid3_write_id3v2 $ID3v2ShortFrameNameLookup[3]['text'] = 'TXXX'; $ID3v2ShortFrameNameLookup[3]['unique_file_identifier'] = 'UFID'; $ID3v2ShortFrameNameLookup[3]['terms_of_use'] = 'USER'; - $ID3v2ShortFrameNameLookup[3]['unsychronised_lyric'] = 'USLT'; + $ID3v2ShortFrameNameLookup[3]['unsynchronised_lyric'] = 'USLT'; $ID3v2ShortFrameNameLookup[3]['commercial_information'] = 'WCOM'; $ID3v2ShortFrameNameLookup[3]['copyright'] = 'WCOP'; $ID3v2ShortFrameNameLookup[3]['url_file'] = 'WOAF'; diff --git a/lib/getid3/write.lyrics3.php b/lib/getid3/write.lyrics3.php index 12275f492f22e7b9e225ad77449c2e7f20c467e6..4c5c86861cbfce50945ae16f04d174daf6e0d4fd 100644 --- a/lib/getid3/write.lyrics3.php +++ b/lib/getid3/write.lyrics3.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.lyrics3.php // @@ -17,26 +17,51 @@ class getid3_write_lyrics3 { + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data; //public $lyrics3_version = 2; // 1 or 2 - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteLyrics3() { $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3'; return false; } + + /** + * @return bool + */ public function DeleteLyrics3() { // Initialize getID3 engine $getID3 = new getID3; $ThisFileInfo = $getID3->analyze($this->filename); if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { - if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { + if (is_readable($this->filename) && getID3::is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { flock($fp, LOCK_EX); $oldignoreuserabort = ignore_user_abort(true); diff --git a/lib/getid3/write.metaflac.php b/lib/getid3/write.metaflac.php index 186e2c17b5a38af0bc591deb0d51d3e8edcfd663..29863344db0148617ce62f18d0792f04f58c02d5 100644 --- a/lib/getid3/write.metaflac.php +++ b/lib/getid3/write.metaflac.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.metaflac.php // @@ -17,16 +17,38 @@ class getid3_write_metaflac { - + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data; - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); + + private $pictures = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteMetaFLAC() { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { @@ -34,9 +56,35 @@ class getid3_write_metaflac return false; } + $tempfilenames = array(); + + + if (!empty($this->tag_data['ATTACHED_PICTURE'])) { + foreach ($this->tag_data['ATTACHED_PICTURE'] as $key => $picturedetails) { + $temppicturefilename = tempnam(GETID3_TEMP_DIR, 'getID3'); + $tempfilenames[] = $temppicturefilename; + if (getID3::is_writable($temppicturefilename) && is_file($temppicturefilename) && ($fpcomments = fopen($temppicturefilename, 'wb'))) { + // https://xiph.org/flac/documentation_tools_flac.html#flac_options_picture + // [TYPE]|[MIME-TYPE]|[DESCRIPTION]|[WIDTHxHEIGHTxDEPTH[/COLORS]]|FILE + fwrite($fpcomments, $picturedetails['data']); + fclose($fpcomments); + $picture_typeid = (!empty($picturedetails['picturetypeid']) ? $this->ID3v2toFLACpictureTypes($picturedetails['picturetypeid']) : 3); // default to "3:Cover (front)" + $picture_mimetype = (!empty($picturedetails['mime']) ? $picturedetails['mime'] : ''); // should be auto-detected + $picture_width_height_depth = ''; + $this->pictures[] = $picture_typeid.'|'.$picture_mimetype.'|'.preg_replace('#[^\x20-\x7B\x7D-\x7F]#', '', $picturedetails['description']).'|'.$picture_width_height_depth.'|'.$temppicturefilename; + } else { + $this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$temppicturefilename.'", "wb")'; + return false; + } + } + unset($this->tag_data['ATTACHED_PICTURE']); + } + + // Create file with new comments $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); - if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { + $tempfilenames[] = $tempcommentsfilename; + if (getID3::is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { foreach ($this->tag_data as $key => $value) { foreach ($value as $commentdata) { fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n"); @@ -65,7 +113,11 @@ class getid3_write_metaflac clearstatcache(); $timestampbeforewriting = filemtime($this->filename); - $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1'; + $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename); + foreach ($this->pictures as $picturecommand) { + $commandline .= ' --import-picture-from='.escapeshellarg($picturecommand); + } + $commandline .= ' '.escapeshellarg($this->filename).' 2>&1'; $metaflacError = `$commandline`; if (empty($metaflacError)) { @@ -81,13 +133,19 @@ class getid3_write_metaflac } else { // It's simpler on *nix - $commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1'; + $commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename); + foreach ($this->pictures as $picturecommand) { + $commandline .= ' --import-picture-from='.escapeshellarg($picturecommand); + } + $commandline .= ' '.escapeshellarg($this->filename).' 2>&1'; $metaflacError = `$commandline`; } // Remove temporary comments file - unlink($tempcommentsfilename); + foreach ($tempfilenames as $tempfilename) { + unlink($tempfilename); + } ignore_user_abort($oldignoreuserabort); if (!empty($metaflacError)) { @@ -100,7 +158,9 @@ class getid3_write_metaflac return true; } - + /** + * @return bool + */ public function DeleteMetaFLAC() { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { @@ -146,7 +206,24 @@ class getid3_write_metaflac return true; } + /** + * @param int $id3v2_picture_typeid + * + * @return int + */ + public function ID3v2toFLACpictureTypes($id3v2_picture_typeid) { + // METAFLAC picture type list is identical to ID3v2 picture type list (as least up to 0x14 "Publisher/Studio logotype") + // http://id3.org/id3v2.4.0-frames (section 4.14) + // https://xiph.org/flac/documentation_tools_flac.html#flac_options_picture + //return (isset($ID3v2toFLACpictureTypes[$id3v2_picture_typeid]) ? $ID3v2toFLACpictureTypes[$id3v2_picture_typeid] : 3); // default: "3: Cover (front)" + return (($id3v2_picture_typeid <= 0x14) ? $id3v2_picture_typeid : 3); // default: "3: Cover (front)" + } + /** + * @param string $originalcommentname + * + * @return string + */ public function CleanmetaflacName($originalcommentname) { // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through @@ -156,7 +233,6 @@ class getid3_write_metaflac // Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function // note: *reg_replace() replaces nulls with empty string (not space) return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname))); - } } diff --git a/lib/getid3/write.php b/lib/getid3/write.php index d4416549f03f245af1b945f6779c93f99236b45a..cc98e5d3289b535d4428fe1c440655b387480968 100644 --- a/lib/getid3/write.php +++ b/lib/getid3/write.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// /// // // write.php // @@ -27,48 +27,113 @@ if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { throw new Exception('write.php depends on getid3.lib.php, which is missing.'); } - -// NOTES: -// -// You should pass data here with standard field names as follows: -// * TITLE -// * ARTIST -// * ALBUM -// * TRACKNUMBER -// * COMMENT -// * GENRE -// * YEAR -// * ATTACHED_PICTURE (ID3v2 only) -// -// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html -// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead -// Pass data here as "TRACKNUMBER" for compatability with all formats - - +/** + * NOTES: + * + * You should pass data here with standard field names as follows: + * * TITLE + * * ARTIST + * * ALBUM + * * TRACKNUMBER + * * COMMENT + * * GENRE + * * YEAR + * * ATTACHED_PICTURE (ID3v2 only) + * The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead + * Pass data here as "TRACKNUMBER" for compatability with all formats + * + * @link http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html + */ class getid3_writetags { - // public - public $filename; // absolute filename of file to write tags to - public $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real') - public $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis') - public $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ) - public $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data - public $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats - - public $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html) - public $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter) - - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here - - // private - private $ThisFileInfo; // analysis of file before writing + /** + * Absolute filename of file to write tags to. + * + * @var string + */ + public $filename; + + /** + * Array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', + * 'metaflac', 'real'). + * + * @var array + */ + public $tagformats = array(); + + /** + * 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis'). + * + * @var array + */ + public $tag_data = array(array()); + + /** + * Text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ). + * + * @var string + */ + public $tag_encoding = 'ISO-8859-1'; + + /** + * If true will erase existing tag data and write only passed data; if false will merge passed data + * with existing tag data. + * + * @var bool + */ + public $overwrite_tags = true; + + /** + * If true will erase remove all existing tags and only write those passed in $tagformats; + * If false will ignore any tags not mentioned in $tagformats. + * + * @var bool + */ + public $remove_other_tags = false; + + /** + * ISO-639-2 3-character language code needed for some ID3v2 frames. + * + * @link http://www.id3.org/iso639-2.html + * + * @var string + */ + public $id3v2_tag_language = 'eng'; + + /** + * Minimum length of ID3v2 tags (will be padded to this length if tag data is shorter). + * + * @var int + */ + public $id3v2_paddedlength = 4096; + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); + + /** + * Analysis of file before writing. + * + * @var array + */ + private $ThisFileInfo; public function __construct() { - return true; } - + /** + * @return bool + */ public function WriteTags() { if (empty($this->filename)) { @@ -83,8 +148,23 @@ class getid3_writetags $this->errors[] = 'tagformats must be an array in getid3_writetags'; return false; } + // prevent duplicate tag formats + $this->tagformats = array_unique($this->tagformats); + + // prevent trying to specify more than one version of ID3v2 tag to write simultaneously + $id3typecounter = 0; + foreach ($this->tagformats as $tagformat) { + if (substr(strtolower($tagformat), 0, 6) == 'id3v2.') { + $id3typecounter++; + } + } + if ($id3typecounter > 1) { + $this->errors[] = 'tagformats must not contain more than one version of ID3v2'; + return false; + } $TagFormatsToRemove = array(); + $AllowedTagFormats = array(); if (filesize($this->filename) == 0) { // empty file special case - allow any tag format, don't check existing format @@ -248,7 +328,7 @@ class getid3_writetags switch ($tagformat) { case 'ape': $ape_writer = new getid3_write_apetag; - if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) { + if ($ape_writer->tag_data = $this->FormatDataForAPE()) { $ape_writer->filename = $this->filename; if (($success = $ape_writer->WriteAPEtag()) === false) { $this->errors[] = 'WriteAPEtag() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $ape_writer->errors)))).'</li></ul></pre>'; @@ -260,7 +340,7 @@ class getid3_writetags case 'id3v1': $id3v1_writer = new getid3_write_id3v1; - if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) { + if ($id3v1_writer->tag_data = $this->FormatDataForID3v1()) { $id3v1_writer->filename = $this->filename; if (($success = $id3v1_writer->WriteID3v1()) === false) { $this->errors[] = 'WriteID3v1() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v1_writer->errors)))).'</li></ul></pre>'; @@ -276,7 +356,10 @@ class getid3_writetags $id3v2_writer = new getid3_write_id3v2; $id3v2_writer->majorversion = intval(substr($tagformat, -1)); $id3v2_writer->paddedlength = $this->id3v2_paddedlength; - if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) { + $id3v2_writer_tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion); + if ($id3v2_writer_tag_data !== false) { + $id3v2_writer->tag_data = $id3v2_writer_tag_data; + unset($id3v2_writer_tag_data); $id3v2_writer->filename = $this->filename; if (($success = $id3v2_writer->WriteID3v2()) === false) { $this->errors[] = 'WriteID3v2() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v2_writer->errors)))).'</li></ul></pre>'; @@ -288,7 +371,7 @@ class getid3_writetags case 'vorbiscomment': $vorbiscomment_writer = new getid3_write_vorbiscomment; - if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) { + if ($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) { $vorbiscomment_writer->filename = $this->filename; if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) { $this->errors[] = 'WriteVorbisComment() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $vorbiscomment_writer->errors)))).'</li></ul></pre>'; @@ -300,7 +383,7 @@ class getid3_writetags case 'metaflac': $metaflac_writer = new getid3_write_metaflac; - if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) { + if ($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) { $metaflac_writer->filename = $this->filename; if (($success = $metaflac_writer->WriteMetaFLAC()) === false) { $this->errors[] = 'WriteMetaFLAC() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $metaflac_writer->errors)))).'</li></ul></pre>'; @@ -312,7 +395,7 @@ class getid3_writetags case 'real': $real_writer = new getid3_write_real; - if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) { + if ($real_writer->tag_data = $this->FormatDataForReal()) { $real_writer->filename = $this->filename; if (($success = $real_writer->WriteReal()) === false) { $this->errors[] = 'WriteReal() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $real_writer->errors)))).'</li></ul></pre>'; @@ -335,7 +418,11 @@ class getid3_writetags } - + /** + * @param string[] $TagFormatsToDelete + * + * @return bool + */ public function DeleteTags($TagFormatsToDelete) { foreach ($TagFormatsToDelete as $DeleteTagFormat) { $success = false; // overridden if tag deletion is successful @@ -397,7 +484,7 @@ class getid3_writetags break; default: - $this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"'; + $this->errors[] = 'Invalid tag format to delete: "'.$DeleteTagFormat.'"'; return false; break; } @@ -408,13 +495,19 @@ class getid3_writetags return true; } - + /** + * @param string $TagFormat + * @param array $tag_data + * + * @return bool + * @throws Exception + */ public function MergeExistingTagData($TagFormat, &$tag_data) { // Merge supplied data with existing data, if requested if ($this->overwrite_tags) { // do nothing - ignore previous data } else { -throw new Exception('$this->overwrite_tags=false is known to be buggy in this version of getID3. Will be fixed in the near future, check www.getid3.org for a newer version.'); + throw new Exception('$this->overwrite_tags=false is known to be buggy in this version of getID3. Check http://github.com/JamesHeinrich/getID3 for a newer version.'); if (!isset($this->ThisFileInfo['tags'][$TagFormat])) { return false; } @@ -423,6 +516,9 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve return true; } + /** + * @return array + */ public function FormatDataForAPE() { $ape_tag_data = array(); foreach ($this->tag_data as $tag_key => $valuearray) { @@ -449,8 +545,11 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve return $ape_tag_data; } - + /** + * @return array + */ public function FormatDataForID3v1() { + $tag_data_id3v1 = array(); $tag_data_id3v1['genreid'] = 255; if (!empty($this->tag_data['GENRE'])) { foreach ($this->tag_data['GENRE'] as $key => $value) { @@ -474,6 +573,11 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve return $tag_data_id3v1; } + /** + * @param int $id3v2_majorversion + * + * @return array|false + */ public function FormatDataForID3v2($id3v2_majorversion) { $tag_data_id3v2 = array(); @@ -605,6 +709,9 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve return $tag_data_id3v2; } + /** + * @return array + */ public function FormatDataForVorbisComment() { $tag_data_vorbiscomment = $this->tag_data; @@ -612,21 +719,25 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve // and convert data to UTF-8 strings foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) { foreach ($valuearray as $key => $value) { - str_replace("\r", "\n", $value); - if (strstr($value, "\n")) { - unset($tag_data_vorbiscomment[$tag_key][$key]); - $multilineexploded = explode("\n", $value); - foreach ($multilineexploded as $newcomment) { - if (strlen(trim($newcomment)) > 0) { - $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment); + if (($tag_key == 'ATTACHED_PICTURE') && is_array($value)) { + continue; // handled separately in write.metaflac.php + } else { + str_replace("\r", "\n", $value); + if (strstr($value, "\n")) { + unset($tag_data_vorbiscomment[$tag_key][$key]); + $multilineexploded = explode("\n", $value); + foreach ($multilineexploded as $newcomment) { + if (strlen(trim($newcomment)) > 0) { + $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment); + } } + } elseif (is_string($value) || is_numeric($value)) { + $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); + } else { + $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag'; + unset($tag_data_vorbiscomment[$tag_key]); + break; } - } elseif (is_string($value) || is_numeric($value)) { - $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); - } else { - $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag'; - unset($tag_data_vorbiscomment[$tag_key]); - break; } } } @@ -634,12 +745,18 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve return $tag_data_vorbiscomment; } + /** + * @return array + */ public function FormatDataForMetaFLAC() { // FLAC & OggFLAC use VorbisComments same as OggVorbis // but require metaflac to do the writing rather than vorbiscomment return $this->FormatDataForVorbisComment(); } + /** + * @return array + */ public function FormatDataForReal() { $tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array()))); $tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array()))); diff --git a/lib/getid3/write.real.php b/lib/getid3/write.real.php index fd67c8591052129a197b46bc50a531116b94c303..38c0648c738b448c8cf911e26a55d0dd94d43d5b 100644 --- a/lib/getid3/write.real.php +++ b/lib/getid3/write.real.php @@ -1,11 +1,11 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.real.php // @@ -16,20 +16,53 @@ class getid3_write_real { + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data = array(); - public $fread_buffer_size = 32768; // read buffer size in bytes - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here - public $paddedlength = 512; // minimum length of CONT tag in bytes + + /** + * Read buffer size in bytes. + * + * @var int + */ + public $fread_buffer_size = 32768; + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); + + /** + * Minimum length of CONT tag in bytes. + * + * @var int + */ + public $paddedlength = 512; public function __construct() { - return true; } + /** + * @return bool + */ public function WriteReal() { // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { // Initialize getID3 engine $getID3 = new getID3; @@ -45,6 +78,7 @@ class getid3_write_real fclose($fp_source); return false; } + $oldChunkInfo = array(); foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { $oldChunkInfo[$chunkarray['name']] = $chunkarray; } @@ -94,7 +128,7 @@ class getid3_write_real $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; } if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { + if (getID3::is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { rewind($fp_source); fwrite($fp_temp, fread($fp_source, $BeforeOffset)); @@ -127,8 +161,14 @@ class getid3_write_real return false; } + /** + * @param array $chunks + * + * @return string + */ public function GenerateRMFchunk(&$chunks) { $oldCONTexists = false; + $chunkNameKeys = array(); foreach ($chunks as $key => $chunk) { $chunkNameKeys[$chunk['name']] = $key; if ($chunk['name'] == 'CONT') { @@ -145,10 +185,17 @@ class getid3_write_real return $RMFchunk; } + /** + * @param array $chunks + * @param string $new_CONT_tag_data + * + * @return string + */ public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) { $old_CONT_length = 0; $old_DATA_offset = 0; $old_INDX_offset = 0; + $chunkNameKeys = array(); foreach ($chunks as $key => $chunk) { $chunkNameKeys[$chunk['name']] = $key; if ($chunk['name'] == 'CONT') { @@ -182,6 +229,9 @@ class getid3_write_real return $PROPchunk; } + /** + * @return string + */ public function GenerateCONTchunk() { foreach ($this->tag_data as $key => $value) { // limit each value to 0xFFFF bytes @@ -211,9 +261,12 @@ class getid3_write_real return $CONTchunk; } + /** + * @return bool + */ public function RemoveReal() { // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { + if (getID3::is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { // Initialize getID3 engine $getID3 = new getID3; @@ -242,7 +295,7 @@ class getid3_write_real $BeforeOffset = $oldChunkInfo['CONT']['offset']; $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { + if (getID3::is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) { rewind($fp_source); fwrite($fp_temp, fread($fp_source, $BeforeOffset)); diff --git a/lib/getid3/write.vorbiscomment.php b/lib/getid3/write.vorbiscomment.php index 971f91fe7b61b39c4cc7b0671c1c26a28beb0016..5f89868cc3d61658ffeb2f737378f85d14516df2 100644 --- a/lib/getid3/write.vorbiscomment.php +++ b/lib/getid3/write.vorbiscomment.php @@ -1,11 +1,12 @@ <?php + ///////////////////////////////////////////////////////////////// /// getID3() by James Heinrich <info@getid3.org> // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// also https://github.com/JamesHeinrich/getID3 // +// available at https://github.com/JamesHeinrich/getID3 // +// or https://www.getid3.org // +// or http://getid3.sourceforge.net // ///////////////////////////////////////////////////////////////// -// See readme.txt for more details // +// see readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // write.vorbiscomment.php // @@ -17,16 +18,36 @@ class getid3_write_vorbiscomment { - + /** + * @var string + */ public $filename; + + /** + * @var array + */ public $tag_data; - public $warnings = array(); // any non-critical errors will be stored here - public $errors = array(); // any critical errors will be stored here + + /** + * Any non-critical errors will be stored here. + * + * @var array + */ + public $warnings = array(); + + /** + * Any critical errors will be stored here. + * + * @var array + */ + public $errors = array(); public function __construct() { - return true; } + /** + * @return bool + */ public function WriteVorbisComment() { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { @@ -36,7 +57,7 @@ class getid3_write_vorbiscomment // Create file with new comments $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); - if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { + if (getID3::is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { foreach ($this->tag_data as $key => $value) { foreach ($value as $commentdata) { @@ -100,11 +121,19 @@ class getid3_write_vorbiscomment return true; } + /** + * @return bool + */ public function DeleteVorbisComment() { $this->tag_data = array(array()); return $this->WriteVorbisComment(); } + /** + * @param string $originalcommentname + * + * @return string + */ public function CleanVorbisCommentName($originalcommentname) { // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through