diff options
author | Paul Arthur <paul.arthur@flowerysong.com> | 2011-10-27 20:41:26 -0400 |
---|---|---|
committer | Paul Arthur <paul.arthur@flowerysong.com> | 2011-10-27 20:41:26 -0400 |
commit | 51893b1ee5cd38a6fbd2d22d7659d0bfee4de038 (patch) | |
tree | aba5e9ed95ea942a5e9cb7e47d05b2d7798779dd | |
parent | 8e89cb990a105479aa307c656a509eebc8537e55 (diff) | |
download | ampache-51893b1ee5cd38a6fbd2d22d7659d0bfee4de038.tar.gz ampache-51893b1ee5cd38a6fbd2d22d7659d0bfee4de038.tar.bz2 ampache-51893b1ee5cd38a6fbd2d22d7659d0bfee4de038.zip |
Update getID3 to 1.9.1
72 files changed, 6365 insertions, 4594 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG index c7ba00ae..ccab3936 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -4,6 +4,7 @@ -------------------------------------------------------------------------- v.3.6-Alpha2 + - Updated getID3 to 1.9.1 - Updated PHPMailer to 5.2.0 - Fixed bug in MPD module which affected toggling random or repeat (patch from jherold) diff --git a/modules/getid3/docs/changelog.txt b/modules/getid3/docs/changelog.txt index 797b032e..556c6f47 100644 --- a/modules/getid3/docs/changelog.txt +++ b/modules/getid3/docs/changelog.txt @@ -17,6 +17,75 @@ Version History =============== +1.9.1: [2011-08-10] James Heinrich + ¤ ASF Extended Header Object data now (partially) parsed + * Default getID3 encoding now set to UTF-8 not ISO-8859-1 + * Bugfix: (#1212) truncated Matroska files may result in + infinite loop and memory exhaustion + * Bugfix: (#1203) parse RIFF JUNK chunks for version strings + * Bugfix: (#1201) multi-byte characters strings incorrectly + displayed by table_var_dump() in demo.browse.php + * Bugfix: (#1199) prevent PHP warning on malformed ID3v2 APIC + * Bugfix: (#1196) typo in module.audio-video.quicktime.php + * Bugfix: (#1195) QuicktimeStoreFrontCodeLookup() broken + * Bugfix: (#1194) mp4 embedded images not handled correctly + * Bugfix: (#1193) [image_mime] key not set fo WM/picture data + * Bugfix: (#1193) ASF Extended Header Object Metadata Library + now parsed for embedded images and handled per usual style + * Bugfix: (#1190) demo.mimeonly.php was broken since v1.9.0 + * Bugfix: ID3v2 comment is now called 'comment' not 'comments' + * Bugfix: AVI unknown codec fourcc would be reported as blank + * Bugfix: AVI zero-size JUNK chunk would give warning + + +1.9.0: [2011-06-20] James Heinrich + » changed all module classes to have proper constructors + with the actual analysis code moved to function Analyze() + * removed unnecessary ob_* calls, replaced with appropriate + checks and judicious use of @ error suppression + ¤ GETID3_VERSION constant replaced with $getID3->version() + ¤ picture data is now returned only in the original source + location and [comments][picture], it is no longer replicated + in [comments_html], [tags] or [tags_html] + ¤ Matroska tags are now returned in [comments] as per normal + ¤ Matroska tags are better supported, including pictures + ¤ GPS data in MP4 files (e.g. iPhone) is now parsed (#1157) + ¤ Matroska audio/video tracks with a default flag, the default + stream flag is now copied to [audio|video][streams] (#1147) + ¤ Nikon-specific data (NCDT atom) in Quicktime videos now parsed + ¤ QuickTime atoms 'meta' and 'data' now (mostly) parsed + * Bugfix: remove false warning of junk data on WAV+ID3v1 + * Bugfix: DolbyDigitalWAV files returned wrong audio bitrate + * Bugfix: large attachment data in Matroska tags were not + returned completely. + * Bugfix: wrong image_mime used for images in demo.browse.php + * Bugfix: broken preg_match in module.audio.dss.php + * Bugfix: Lyrics3 end offset was off by 1 + * Bugfix: audio channelmode could be wrong for 2 channels + (e.g. joint stereo reported as stereo) + * Bugfix: MultiByteCharString2HTML() would return empty string + if passed float or int value, now casts to string first + * Bugfix: FLAC.picture was not returning under [data] + + [image_mime] per standardized format + * Bugfix: BigEndian2Int() could incorrectly return negative + signed synchsafe integer instead of casting to float + * Bugfix: (#1177) ID3v2.4 extended headers were broken + * Bugfix: (#1173) some MIDI files not completely parsed + * Bugfix: (#1171) change helperapps error to nonblocking warning + * Bugfix: (#1170) possible infinite loop in FLV module + * Bugfix: (#1169) $this reference in static function (ID3v2) + * Bugfix: (#1156) demo.mysql.php not working + * Bugfix: (#1153) badly-tagged files could produce invalid + argument errors in module.tag.xmp.php + * Bugfix: (#1152) add error-suppression to iconv() calls + * Bugfix: (#1151) AAC-ADTS files could sometimes not find sync + * Bugfix: (#1136) last character of unicode tags (e.g. ASF) + was being truncated + * Bugfix: (#1133) write.id3v2.php IsValidURL() was broken + * Bugfix: (#1126) ID3v2.POPM field was being clobbered + * Bugfix: (#999, #1154) ID3v2 UFID data was missing + + 1.8.5: [2011-02-18] James Heinrich » support >2GB files on 64-bit PHP - note current unofficial 64-bit PHP builds for Windows diff --git a/modules/getid3/docs/readme.txt b/modules/getid3/docs/readme.txt index 06095c53..41397bcf 100644 --- a/modules/getid3/docs/readme.txt +++ b/modules/getid3/docs/readme.txt @@ -578,4 +578,5 @@ Reference material: * http://pda.etsi.org/pda/queryform.asp * http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm * http://trac.musepack.net/trac/wiki/SV8Specification -* http://wyday.com/cuesharp/specification.php
\ No newline at end of file +* http://wyday.com/cuesharp/specification.php +* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html diff --git a/modules/getid3/extension.cache.dbm.php b/modules/getid3/extension.cache.dbm.php index a85cb0cc..3fb1bc46 100644 --- a/modules/getid3/extension.cache.dbm.php +++ b/modules/getid3/extension.cache.dbm.php @@ -120,7 +120,7 @@ class getID3_cached_dbm extends getID3 } // Insert getID3 version number - dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); + dba_insert($this::VERSION, $this::VERSION, $this->dba); } // Init misc values @@ -131,7 +131,7 @@ class getID3_cached_dbm extends getID3 register_shutdown_function(array($this, '__destruct')); // Check version number and clear cache if changed - if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) { + if (dba_fetch($this::VERSION, $this->dba) != $this::VERSION) { $this->clear_cache(); } @@ -169,7 +169,7 @@ class getID3_cached_dbm extends getID3 } // Insert getID3 version number - dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); + dba_insert($this::VERSION, $this::VERSION, $this->dba); // Re-register shutdown function register_shutdown_function(array($this, '__destruct')); diff --git a/modules/getid3/extension.cache.mysql.php b/modules/getid3/extension.cache.mysql.php index e070d838..f61e1760 100644 --- a/modules/getid3/extension.cache.mysql.php +++ b/modules/getid3/extension.cache.mysql.php @@ -101,10 +101,10 @@ class getID3_cached_mysql extends getID3 // Check version number and clear cache if changed $version = ''; - if ($this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".mysql_real_escape_string(GETID3_VERSION)."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection)) { + if ($this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".mysql_real_escape_string($this::VERSION)."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection)) { list($version) = mysql_fetch_array($this->cursor); } - if ($version != GETID3_VERSION) { + if ($version != $this::VERSION) { $this->clear_cache(); } @@ -117,7 +117,7 @@ class getID3_cached_mysql extends getID3 function clear_cache() { $this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection); - $this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection); + $this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".$this::VERSION."', -1, -1, -1, '".$this::VERSION."')", $this->connection); } diff --git a/modules/getid3/getid3.lib.php b/modules/getid3/getid3.lib.php index 13353ed2..f5d15088 100644 --- a/modules/getid3/getid3.lib.php +++ b/modules/getid3/getid3.lib.php @@ -14,7 +14,7 @@ class getid3_lib { - static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) { + static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') { $returnstring = ''; for ($i = 0; $i < strlen($string); $i++) { if ($hex) { @@ -26,8 +26,11 @@ class getid3_lib $returnstring .= ' '; } } - if ($htmlsafe) { - $returnstring = htmlentities($returnstring); + if (!empty($htmlsafe)) { + if ($htmlencoding === true) { + $htmlencoding = 'UTF-8'; + } + $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding); } return $returnstring; } @@ -73,20 +76,20 @@ class getid3_lib return $floatnum; } - public static function intValueSupported($num) {
- // check if integers are 64-bit
- static $hasINT64 = null;
- if ($hasINT64 === null) { // 10x faster than is_null()
+ public static function intValueSupported($num) { + // check if integers are 64-bit + static $hasINT64 = null; + if ($hasINT64 === null) { // 10x faster than is_null() $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1 if (!$hasINT64 && !defined('PHP_INT_MIN')) { define('PHP_INT_MIN', ~PHP_INT_MAX); - }
- }
- // if integers are 64-bit - no other check required
- if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
- return true;
- }
- return false;
+ } + } + // if integers are 64-bit - no other check required + if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) { + return true; + } + return false; } static function DecimalizeFraction($fraction) { @@ -264,7 +267,8 @@ class getid3_lib } for ($i = 0; $i < $bytewordlen; $i++) { if ($synchsafe) { // disregard MSB, effectively 7-bit bytes - $intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); + //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems + $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7); } else { $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i)); } @@ -411,6 +415,16 @@ class getid3_lib } + static function ksort_recursive(&$theArray) { + ksort($theArray); + foreach ($theArray as $key => $value) { + if (is_array($value)) { + self::ksort_recursive($theArray[$key]); + } + } + return true; + } + static function fileextension($filename, $numextensions=1) { if (strstr($filename, '.')) { $reversedfilename = strrev($filename); @@ -513,6 +527,7 @@ class getid3_lib // Allan Hansen <ahŘartemis*dk> // getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position static function hash_data($file, $offset, $end, $algorithm) { + static $tempdir = ''; if (!getid3_lib::intValueSupported($end)) { return false; } @@ -564,15 +579,22 @@ class getid3_lib } if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { - $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm'; + //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm'); break; } return substr(`$commandline`, 0, $hash_length); } + if (empty($tempdir)) { + // yes this is ugly, feel free to suggest a better way + require_once(dirname(__FILE__).'/getid3.php'); + $getid3_temp = new getID3(); + $tempdir = $getid3_temp->tempdir; + unset($getid3_temp); + } // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir - if (($data_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) { - // can't find anywhere to create a temp file, just die + if (($data_filename = tempnam($tempdir, 'gI3')) === false) { + // can't find anywhere to create a temp file, just fail return false; } @@ -580,33 +602,43 @@ class getid3_lib $result = false; // copy parts of file - ob_start(); - if ($fp = fopen($file, 'rb')) { - ob_end_clean(); - ob_start(); - if ($fp_data = fopen($data_filename, 'wb')) { - fseek($fp, $offset, SEEK_SET); - $byteslefttowrite = $end - $offset; - while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) { - $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite); - $byteslefttowrite -= $byteswritten; + try { + getid3_lib::CopyFileParts($file, $data_filename, $offset, $end - $offset); + $result = $hash_function($data_filename); + } catch (Exception $e) { + throw new Exception('getid3_lib::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage()); + } + unlink($data_filename); + return $result; + } + + static function CopyFileParts($filename_source, $filename_dest, $offset, $length) { + if (!getid3_lib::intValueSupported($offset + $length)) { + throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); + } + if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) { + if (is_writable($filename_dest) && is_file($filename_dest) && ($fp_dest = fopen($filename_dest, 'wb'))) { + if (fseek($fp_src, $offset, SEEK_SET) == 0) { + $byteslefttowrite = $length; + while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, $this->getid3->fread_buffer_size())))) { + $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); + $byteslefttowrite -= $byteswritten; + } + return true; + } else { + throw new Exception('failed to seek to offset '.$offset.' in '.$this->info['filenamepath']); } - fclose($fp_data); - $result = $hash_function($data_filename); + fclose($fp_dest); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); + throw new Exception('failed to open file for reading '.$this->info['filenamepath']); } - fclose($fp); + fclose($fp_src); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); + throw new Exception('failed to create file for writing '.$dest); } - unlink($data_filename); - return $result; + return false; } - static function iconv_fallback_int_utf8($charval) { if ($charval < 128) { // 0bbbbbbb @@ -897,19 +929,13 @@ class getid3_lib // iconv() availble if (function_exists('iconv')) { - - ob_start(); - if ($converted_string = iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { - ob_end_clean(); + if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { switch ($out_charset) { case 'ISO-8859-1': $converted_string = rtrim($converted_string, "\x00"); break; } return $converted_string; - } else { - $errormessage = ob_get_contents(); - ob_end_clean(); } // iconv() may sometimes fail with "illegal character in input string" error message @@ -945,6 +971,7 @@ class getid3_lib 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 = ''; switch ($charset) { @@ -1092,20 +1119,20 @@ class getid3_lib static function GetDataImageSize($imgData, &$imageinfo) { + static $tempdir = ''; + if (empty($tempdir)) { + // yes this is ugly, feel free to suggest a better way + require_once(dirname(__FILE__).'/getid3.php'); + $getid3_temp = new getID3(); + $tempdir = $getid3_temp->tempdir; + unset($getid3_temp); + } $GetDataImageSize = false; - if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - ob_start(); - if ($tmp = fopen($tempfilename, 'wb')) { - ob_end_clean(); + if ($tempfilename = tempnam($tempdir, 'gI3')) { + if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) { fwrite($tmp, $imgData); fclose($tmp); - ob_start(); - $GetDataImageSize = GetImageSize($tempfilename, $imageinfo); - $errormessage = ob_get_contents(); - ob_end_clean(); - } else { - $errormessage = ob_get_contents(); - ob_end_clean(); + $GetDataImageSize = @GetImageSize($tempfilename, $imageinfo); } unlink($tempfilename); } @@ -1179,6 +1206,11 @@ class getid3_lib // Copy to ['comments_html'] foreach ($ThisFileInfo['comments'] as $field => $values) { + if ($field == 'picture') { + // pictures can take up a lot of space, and we don't need multiple copies of them + // let there be a single copy in [comments][picture], and not elsewhere + continue; + } foreach ($values as $index => $value) { if (is_array($value)) { $ThisFileInfo['comments_html'][$field][$index] = $value; @@ -1188,6 +1220,7 @@ class getid3_lib } } } + return true; } diff --git a/modules/getid3/getid3.php b/modules/getid3/getid3.php index 22fb7e16..03a18855 100644 --- a/modules/getid3/getid3.php +++ b/modules/getid3/getid3.php @@ -9,10 +9,6 @@ // /// ///////////////////////////////////////////////////////////////// -// Defines -define('GETID3_VERSION', '1.8.5-20110218'); -define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes - // 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))) { @@ -81,35 +77,45 @@ if (!defined('GETID3_INCLUDEPATH')) { class getID3 { // public: Settings - var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv()) - // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE - - var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' - - var $tempdir = GETID3_TEMP_DIR; + 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. - var $option_tag_id3v1 = true; // Read and process ID3v1 tags - var $option_tag_id3v2 = true; // Read and process ID3v2 tags - var $option_tag_lyrics3 = true; // Read and process Lyrics3 tags - var $option_tag_apetag = true; // Read and process APE tags - var $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding - var $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities + 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 - var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc + 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 - var $option_md5_data = false; // Get MD5 sum of data part - slow - var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG - var $option_sha1_data = false; // Get SHA1 sum of data part - slow - var $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP + 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 variables + public $filename; // Filename of file being analysed. + public $fp; // Filepointer to file being analysed. + public $info; // Result array. - // private - var $filename = ''; - var $startup_error = ''; - var $startup_warning = ''; + // Protected variables + protected $startup_error = ''; + protected $startup_warning = ''; + protected $memory_limit = 0; + const VERSION = '1.9.1-20110810'; + const FREAD_BUFFER_SIZE = 32768; // Read buffer size in bytes. + var $tempdir = GETID3_TEMP_DIR; + + const ATTACHMENTS_NONE = false; + const ATTACHMENTS_INLINE = true; // public: constructor function getID3() { @@ -123,19 +129,19 @@ class getID3 } // Check memory - $memory_limit = ini_get('memory_limit'); - if (preg_match('#([0-9]+)M#i', $memory_limit, $matches)) { + $this->memory_limit = ini_get('memory_limit'); + if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) { // could be stored as "16M" rather than 16777216 for example - $memory_limit = $matches[1] * 1048576; - } elseif (preg_match('#([0-9]+)G#i', $memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0 + $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 // could be stored as "2G" rather than 2147483648 for example - $memory_limit = $matches[1] * 1073741824; + $this->memory_limit = $matches[1] * 1073741824; } - if ($memory_limit <= 0) { + if ($this->memory_limit <= 0) { // memory limits probably disabled - } elseif ($memory_limit <= 4194304) { + } elseif ($this->memory_limit <= 4194304) { $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini'; - } elseif ($memory_limit <= 12582912) { + } elseif ($this->memory_limit <= 12582912) { $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'; } @@ -196,7 +202,7 @@ class getID3 $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path if (!is_dir($helperappsdir)) { - $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; + $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; } elseif (strpos(realpath($helperappsdir), ' ') !== false) { $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir)); $path_so_far = array(); @@ -216,7 +222,7 @@ class getID3 } } } else { - $this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.'; + $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.'; } } $path_so_far[] = $value; @@ -226,6 +232,28 @@ class getID3 define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR); } + return true; + } + + function version() { + $version = getID3::VERSION; + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + // can't use this syntax, even conditionally, since it registers as a parse error before PHP v5.3.0 + // wrapping the new syntax in an eval call should work without causing parse errors in old PHP + // return $this::VERSION; + eval('$version = $this::VERSION;'); + } + return $version; + } + function fread_buffer_size() { + $fread_buffer_size = getID3::FREAD_BUFFER_SIZE; + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + // can't use this syntax, even conditionally, since it registers as a parse error before PHP v5.3.0 + // wrapping the new syntax in an eval call should work without causing parse errors in old PHP + // return $this::VERSION; + eval('$fread_buffer_size = $this::FREAD_BUFFER_SIZE;'); + } + return $fread_buffer_size; } @@ -235,7 +263,6 @@ class getID3 return false; } foreach ($optArray as $opt => $val) { - //if (isset($this, $opt) === false) { if (isset($this->$opt) === false) { continue; } @@ -245,8 +272,7 @@ class getID3 } - // public: analyze file - function analyze($filename) { + function openfile($filename) { try { if (!empty($this->startup_error)) { return $this->error($this->startup_error); @@ -256,55 +282,42 @@ class getID3 } // init result array and set parameters + $this->filename = $filename; $this->info = array(); - $this->info['GETID3_VERSION'] = GETID3_VERSION; - - // Check encoding/iconv support - if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { - $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; - if (GETID3_OS_ISWINDOWS) { - $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; - } else { - $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; - } - return $this->error($errormessage); - } + $this->info['GETID3_VERSION'] = $this->version(); + $this->info['php_memory_limit'] = $this->memory_limit; // remote files not supported if (preg_match('/^(ht|f)tp:\/\//', $filename)) { - return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first'); + return $this->error('Remote files are not supported - please copy the file locally first'); } $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename); $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename); // open local file - if (file_exists($filename) && is_file($filename)) { - ob_start(); - if ($fp = fopen($filename, 'rb')) { - // great - ob_end_clean(); - } else { - $fopen_error = ob_get_contents(); - ob_end_clean(); - return $this->error('Could not open file "'.$filename.'" (fopen says: '.$fopen_error.')'); - } + if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { + // great } else { return $this->error('Could not open "'.$filename.'" (does not exist, or is not a file)'); } - // set parameters $this->info['filesize'] = filesize($filename); + // set redundant parameters - might be needed in some include file + $this->info['filename'] = basename($filename); + $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); + $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; + // option_max_2gb_check if ($this->option_max_2gb_check) { // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB) // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer - $fseek = fseek($fp, 0, SEEK_END); - if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($fp) == 0)) || + $fseek = fseek($this->fp, 0, SEEK_END); + if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || ($this->info['filesize'] < 0) || - (ftell($fp) < 0)) { + (ftell($this->fp) < 0)) { $real_filesize = false; if (GETID3_OS_ISWINDOWS) { $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"'; @@ -321,11 +334,11 @@ class getID3 } if ($real_filesize === false) { unset($this->info['filesize']); - fclose($fp); + fclose($this->fp); return $this->error('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.'); } elseif (getid3_lib::intValueSupported($real_filesize)) { unset($this->info['filesize']); - fclose($fp); + fclose($this->fp); return $this->error('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'); } $this->info['filesize'] = $real_filesize; @@ -345,84 +358,73 @@ class getID3 $this->info['comments'] = array(); // filled in later, unset if not used $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired - // set redundant parameters - might be needed in some include file - $this->info['filename'] = basename($filename); - $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); - $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; - - - // handle ID3v2 tag - done first - already at beginning of file - // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect - if ($this->option_tag_id3v2) { - - $GETID3_ERRORARRAY = &$this->info['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) { - $tag = new getid3_id3v2($fp, $this->info); - unset($tag); - } - + } catch (Exception $e) { + if (isset($this->info['error'])) { + $this->info['error'][] = 'Caught exception: '.$e->getMessage(); } else { - - fseek($fp, 0, SEEK_SET); - $header = fread($fp, 10); - if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) { - $this->info['id3v2']['header'] = true; - $this->info['id3v2']['majorversion'] = ord($header{3}); - $this->info['id3v2']['minorversion'] = ord($header{4}); - $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length - - $this->info['id3v2']['tag_offset_start'] = 0; - $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength']; - $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end']; - } - + $this->info['error'] = array('Caught exception: '.$e->getMessage()); } + } + } - - // handle ID3v1 tag - if ($this->option_tag_id3v1) { - if (!file_exists(GETID3_INCLUDEPATH.'module.tag.id3v1.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) { - return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.'); + // public: analyze file + function analyze($filename) { + try { + $this->openfile($filename); + + // Handle tags + foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) { + $option_tag = 'option_tag_'.$tag_name; + if ($this->$option_tag) { + $this->include_module('tag.'.$tag_name); + try { + $tag_class = 'getid3_'.$tag_name; + $tag = new $tag_class($this); + $tag->Analyze(); + } + catch (getid3_exception $e) { + throw $e; + } } - $tag = new getid3_id3v1($fp, $this->info); - unset($tag); } - - // handle APE tag - if ($this->option_tag_apetag) { - if (!file_exists(GETID3_INCLUDEPATH.'module.tag.apetag.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) { - return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.'); + if (isset($this->info['id3v2']['tag_offset_start'])) { + $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']); + } + foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) { + if (isset($this->info[$tag_key]['tag_offset_start'])) { + $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']); } - $tag = new getid3_apetag($fp, $this->info); - unset($tag); } - // handle lyrics3 tag - if ($this->option_tag_lyrics3) { - if (!file_exists(GETID3_INCLUDEPATH.'module.tag.lyrics3.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) { - return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.'); + // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier + if (!$this->option_tag_id3v2) { + fseek($this->fp, 0, SEEK_SET); + $header = fread($this->fp, 10); + if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) { + $this->info['id3v2']['header'] = true; + $this->info['id3v2']['majorversion'] = ord($header{3}); + $this->info['id3v2']['minorversion'] = ord($header{4}); + $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length } - $tag = new getid3_lyrics3($fp, $this->info); - unset($tag); } // read 32 kb file data - fseek($fp, $this->info['avdataoffset'], SEEK_SET); - $formattest = fread($fp, 32774); + fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); + $formattest = fread($this->fp, 32774); // determine format $determined_format = $this->GetFileFormat($formattest, $filename); // unable to determine file format if (!$determined_format) { - fclose($fp); + fclose($this->fp); return $this->error('unable to determine file format'); } // check for illegal ID3 tags if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { if ($determined_format['fail_id3'] === 'ERROR') { - fclose($fp); + fclose($this->fp); return $this->error('ID3 tags not allowed on this file type.'); } elseif ($determined_format['fail_id3'] === 'WARNING') { $this->info['warning'][] = 'ID3 tags not allowed on this file type.'; @@ -432,7 +434,7 @@ class getID3 // check for illegal APE tags if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { if ($determined_format['fail_ape'] === 'ERROR') { - fclose($fp); + fclose($this->fp); return $this->error('APE tags not allowed on this file type.'); } elseif ($determined_format['fail_ape'] === 'WARNING') { $this->info['warning'][] = 'APE tags not allowed on this file type.'; @@ -444,13 +446,20 @@ class getID3 // supported format signature pattern detected, but module deleted if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { - fclose($fp); + fclose($this->fp); return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.'); } // module requires iconv support - if (!function_exists('iconv') && !empty($determined_format['iconv_req'])) { - return $this->error('iconv support is required for this module ('.$determined_format['include'].').'); + // Check encoding/iconv support + if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { + $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; + if (GETID3_OS_ISWINDOWS) { + $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; + } else { + $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; + } + return $this->error($errormessage); } // include module @@ -461,15 +470,22 @@ class getID3 if (!class_exists($class_name)) { return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.'); } - if (isset($determined_format['option'])) { - $class = new $class_name($fp, $this->info, $determined_format['option']); - } else { - $class = new $class_name($fp, $this->info); + //if (isset($determined_format['option'])) { + // //$class = new $class_name($this->fp, $this->info, $determined_format['option']); + //} else { + //$class = new $class_name($this->fp, $this->info); + $class = new $class_name($this); + //} + + if (!empty($determined_format['set_inline_attachments'])) { + $class->inline_attachments = $this->option_save_attachments; } + $class->Analyze(); + unset($class); // close file - fclose($fp); + fclose($this->fp); // process all tags - copy to 'tags' and convert charsets if ($this->option_tags_process) { @@ -562,6 +578,19 @@ class getID3 unset($this->info['avdataend']); } } + + // remove possible duplicated identical entries + if (!empty($this->info['error'])) { + $this->info['error'] = array_values(array_unique($this->info['error'])); + } + if (!empty($this->info['warning'])) { + $this->info['warning'] = array_values(array_unique($this->info['warning'])); + } + + // remove "global variable" type keys + unset($this->info['php_memory_limit']); + + return true; } @@ -586,7 +615,6 @@ class getID3 'pattern' => '^ADIF', 'group' => 'audio', 'module' => 'aac', - 'option' => 'adif', 'mime_type' => 'application/octet-stream', 'fail_ape' => 'WARNING', ), @@ -605,7 +633,6 @@ class getID3 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', 'group' => 'audio', 'module' => 'aac', - 'option' => 'adts', 'mime_type' => 'application/octet-stream', 'fail_ape' => 'WARNING', ), @@ -657,6 +684,7 @@ class getID3 'group' => 'audio', 'module' => 'flac', 'mime_type' => 'audio/x-flac', + 'set_inline_attachments' => true, ), // LA - audio - Lossless Audio (LA) @@ -706,7 +734,7 @@ class getID3 'pattern' => '^IMPM', 'group' => 'audio', 'module' => 'mod', - 'option' => 'it', + //'option' => 'it', 'mime_type' => 'audio/it', ), @@ -715,7 +743,7 @@ class getID3 'pattern' => '^Extended Module', 'group' => 'audio', 'module' => 'mod', - 'option' => 'xm', + //'option' => 'xm', 'mime_type' => 'audio/xm', ), @@ -724,7 +752,7 @@ class getID3 'pattern' => '^.{44}SCRM', 'group' => 'audio', 'module' => 'mod', - 'option' => 's3m', + //'option' => 's3m', 'mime_type' => 'audio/s3m', ), @@ -836,6 +864,7 @@ class getID3 'group' => 'audio-video', 'module' => 'matroska', 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska + 'set_inline_attachments' => true, ), // MPEG - audio/video - MPEG (Moving Pictures Experts Group) @@ -862,6 +891,7 @@ class getID3 'mime_type' => 'application/ogg', 'fail_id3' => 'WARNING', 'fail_ape' => 'WARNING', + 'set_inline_attachments' => true, ), // QT - audio/video - Quicktime @@ -963,7 +993,7 @@ class getID3 ), - // TIFF - still image - Tagged Information File Format (TIFF) + // TIFF - still image - Tagged Information File Format (TIFF) 'tiff' => array( 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', 'group' => 'graphic', @@ -974,6 +1004,17 @@ class getID3 ), + // EFAX - still image - eFax (TIFF derivative) + 'bmp' => array( + 'pattern' => '^\xDC\xFE', + 'group' => 'graphic', + 'module' => 'efax', + 'mime_type' => 'image/efax', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // Data formats // ISO - data - International Standards Organization (ISO) CD-ROM Image @@ -1170,10 +1211,11 @@ class getID3 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8 'ape' => array('ape' , 'UTF-8'), 'cue' => array('cue' , 'ISO-8859-1'), + 'matroska' => array('matroska' , 'UTF-8'), ); } - // loop thru comments array + // loop through comments array foreach ($tags as $comment_name => $tagname_encoding_array) { list($tag_name, $encoding) = $tagname_encoding_array; @@ -1187,9 +1229,11 @@ class getID3 foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { foreach ($valuearray as $key => $value) { - $value = (is_string($value) ? trim($value) : $value); - if (!empty($value) > 0) { - $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! + if (is_string($value)) { + $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed! + } + if ($value) { + $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; } } } @@ -1204,7 +1248,7 @@ class getID3 foreach ($valuearray as $key => $value) { if (is_string($value)) { //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding); - $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $encoding)); + $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding))); } else { $this->info['tags_html'][$tag_name][$tag_key][$key] = $value; } @@ -1216,6 +1260,51 @@ class getID3 } } + + // pictures can take up a lot of space, and we don't need multiple copies of them + // let there be a single copy in [comments][picture], and not elsewhere + if (!empty($this->info['tags'])) { + $unset_keys = array('tags', 'tags_html'); + foreach ($this->info['tags'] as $tagtype => $tagarray) { + foreach ($tagarray as $tagname => $tagdata) { + if ($tagname == 'picture') { + foreach ($tagdata as $key => $tagarray) { + $this->info['comments']['picture'][] = $tagarray; + if (isset($tagarray['data']) && isset($tagarray['image_mime'])) { + if (isset($this->info['tags'][$tagtype][$tagname][$key])) { + unset($this->info['tags'][$tagtype][$tagname][$key]); + } + if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) { + unset($this->info['tags_html'][$tagtype][$tagname][$key]); + } + } + } + } + } + foreach ($unset_keys as $unset_key) { + // remove possible empty keys from (e.g. [tags][id3v2][picture]) + if (empty($this->info[$unset_key][$tagtype]['picture'])) { + unset($this->info[$unset_key][$tagtype]['picture']); + } + if (empty($this->info[$unset_key][$tagtype])) { + unset($this->info[$unset_key][$tagtype]); + } + if (empty($this->info[$unset_key])) { + unset($this->info[$unset_key]); + } + } + // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture]) + if (isset($this->info[$tagtype]['comments']['picture'])) { + unset($this->info[$tagtype]['comments']['picture']); + } + if (empty($this->info[$tagtype]['comments'])) { + unset($this->info[$tagtype]['comments']); + } + if (empty($this->info[$tagtype])) { + unset($this->info[$tagtype]); + } + } + } return true; } @@ -1347,7 +1436,7 @@ class getID3 function ChannelsBitratePlaytimeCalculations() { // set channelmode on audio - if (!isset($this->info['audio']['channels'])) { + if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) { // ignore } elseif ($this->info['audio']['channels'] == 1) { $this->info['audio']['channelmode'] = 'mono'; @@ -1476,7 +1565,9 @@ class getID3 function CalculateReplayGain() { if (isset($this->info['replay_gain'])) { - $this->info['replay_gain']['reference_volume'] = 89; + if (!isset($this->info['replay_gain']['reference_volume'])) { + $this->info['replay_gain']['reference_volume'] = (double) 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']; } @@ -1511,6 +1602,161 @@ class getID3 return tempnam($this->tempdir, 'gI3'); } + + public function saveAttachment(&$ThisFileInfoIndex, $filename, $offset, $length) { + try { + if (!getid3_lib::intValueSupported($offset + $length)) { + throw new Exception('cannot extract attachment, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); + } + switch ($this->option_save_attachments) { + case getID3::ATTACHMENTS_NONE: // do not extract attachments data + unset($ThisFileInfoIndex); // do not set any + break; + + case getID3::ATTACHMENTS_INLINE: // extract to return array + // get whole data in one pass, till it is anyway stored in memory + $ThisFileInfoIndex = file_get_contents($this->info['filenamepath'], false, null, $offset, $length); + if (($ThisFileInfoIndex === false) || (strlen($ThisFileInfoIndex) != $length)) { // verify + throw new Exception('failed to read attachment data'); + } + break; + + default: // assume directory path is given + $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->option_save_attachments), DIRECTORY_SEPARATOR); + // check supplied directory + if (!is_dir($dir) || !is_writable($dir)) { + throw new Exception('getID3::saveAttachment() -- supplied path ('.$dir.') does not exist, or is not writable'); + } + + // set up destination path + $dest = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $dir).DIRECTORY_SEPARATOR.$filename; + + // optimize speed if read buffer size is configured to be large enough + // here stream_copy_to_stream() may also be used. need to do speed-compare tests + if ($length <= $this->option_read_buffer_size) { + $data = file_get_contents($this->info['filenamepath'], false, null, $offset, $length); + if (($data === false) || (strlen($data) != $length)) { // verify + throw new Exception('failed to read attachment data'); + } + if (!file_put_contents($dest, $data)) { + throw new Exception('failed to create file '.$dest); + } + } else { + // optimization not available - copy data in loop + // here stream_copy_to_stream() shouldn't be used because it's internal read buffer may be larger than ours! + + try { + getid3_lib::CopyFileParts($this->info['filenamepath'], $filename, $offset, $length); + } catch (Exception $e) { + throw $e; + } + } + $ThisFileInfoIndex = $dest; + break; + } + } catch (Exception $e) { + unset($ThisFileInfoIndex); // do not set any is case of error + $this->warning('Failed to extract attachment '.$filename.': '.$e->getMessage()); + return false; + } + return true; + } + + + public function include_module($name) { + //if (!file_exists($this->include_path.'module.'.$name.'.php')) { + if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) { + throw new getid3_exception('Required module.'.$name.'.php is missing.'); + } + include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php'); + return true; + } + +} + + +abstract class getid3_handler +{ + 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 + + + public function __construct(getID3 $getid3) { + $this->getid3 = $getid3; + } + + + // Analyze from file pointer + abstract public function Analyze(); + + + // Analyze from string instead + public function AnalyzeString(&$string) { + // Enter string mode + $this->data_string_flag = true; + $this->data_string = $string; + + // Save info + $saved_avdataoffset = $this->getid3->info['avdataoffset']; + $saved_avdataend = $this->getid3->info['avdataend']; + $saved_filesize = $this->getid3->info['filesize']; + + // Reset some info + $this->getid3->info['avdataoffset'] = 0; + $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string); + + // Analyze + $this->Analyze(); + + // Restore some info + $this->getid3->info['avdataoffset'] = $saved_avdataoffset; + $this->getid3->info['avdataend'] = $saved_avdataend; + $this->getid3->info['filesize'] = $saved_filesize; + + // Exit string mode + $this->data_string_flag = false; + } + + + protected function ftell() { + if ($this->data_string_flag) { + return $this->data_string_position; + } + return ftell($this->getid3->fp); + } + + + protected function fread($bytes) { + if ($this->data_string_flag) { + $this->data_string_position += $bytes; + return substr($this->data_string, $this->data_string_position - $bytes, $bytes); + } + return fread($this->getid3->fp, $bytes); + } + + + protected function fseek($bytes, $whence = SEEK_SET) { + if ($this->data_string_flag) { + switch ($whence) { + case SEEK_SET: + $this->data_string_position = $bytes; + return; + + case SEEK_CUR: + $this->data_string_position += $bytes; + return; + + case SEEK_END: + $this->data_string_position = strlen($this->data_string) + $bytes; + return; + } + } + return fseek($this->getid3->fp, $bytes, $whence); + } + } diff --git a/modules/getid3/module.archive.gzip.php b/modules/getid3/module.archive.gzip.php index 244ceade..c30052ed 100644 --- a/modules/getid3/module.archive.gzip.php +++ b/modules/getid3/module.archive.gzip.php @@ -19,24 +19,28 @@ ///////////////////////////////////////////////////////////////// -class getid3_gzip { +class getid3_gzip extends getid3_handler { // public: Optional file list - disable for speed. var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) - function getid3_gzip(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'gzip'; + function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'gzip'; $start_length = 10; $unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os'; //+---+---+---+---+---+---+---+---+---+---+ //|ID1|ID2|CM |FLG| MTIME |XFL|OS | //+---+---+---+---+---+---+---+---+---+---+ - ob_start(); - fseek($fd, 0); - $buffer = fread($fd, $ThisFileInfo['filesize']); - $errormessage = ob_get_contents(); - ob_end_clean(); + + if ($info['filesize'] > $info['php_memory_limit']) { + $info['error'][] = 'File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)'; + return false; + } + fseek($this->getid3->fp, 0); + $buffer = fread($this->getid3->fp, $info['filesize']); $arr_members = explode("\x1F\x8B\x08", $buffer); while (true) { @@ -62,7 +66,7 @@ class getid3_gzip { } } - $ThisFileInfo['gzip']['files'] = array(); + $info['gzip']['files'] = array(); $fpointer = 0; $idx = 0; @@ -70,29 +74,29 @@ class getid3_gzip { if (strlen($arr_members[$i]) == 0) { continue; } - $thisThisFileInfo = &$ThisFileInfo['gzip']['member_header'][++$idx]; + $thisInfo = &$info['gzip']['member_header'][++$idx]; $buff = "\x1F\x8B\x08".$arr_members[$i]; $attr = unpack($unpack_header, substr($buff, 0, $start_length)); - $thisThisFileInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']); - $thisThisFileInfo['raw']['id1'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['id2'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['cmethod'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['os'] = ord($attr['os']); - $thisThisFileInfo['raw']['xflags'] = ord($attr['xflags']); - $thisThisFileInfo['raw']['flags'] = ord($attr['flags']); + $thisInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']); + $thisInfo['raw']['id1'] = ord($attr['cmethod']); + $thisInfo['raw']['id2'] = ord($attr['cmethod']); + $thisInfo['raw']['cmethod'] = ord($attr['cmethod']); + $thisInfo['raw']['os'] = ord($attr['os']); + $thisInfo['raw']['xflags'] = ord($attr['xflags']); + $thisInfo['raw']['flags'] = ord($attr['flags']); - $thisThisFileInfo['flags']['crc16'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x02); - $thisThisFileInfo['flags']['extra'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x04); - $thisThisFileInfo['flags']['filename'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x08); - $thisThisFileInfo['flags']['comment'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x10); + $thisInfo['flags']['crc16'] = (bool) ($thisInfo['raw']['flags'] & 0x02); + $thisInfo['flags']['extra'] = (bool) ($thisInfo['raw']['flags'] & 0x04); + $thisInfo['flags']['filename'] = (bool) ($thisInfo['raw']['flags'] & 0x08); + $thisInfo['flags']['comment'] = (bool) ($thisInfo['raw']['flags'] & 0x10); - $thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']); + $thisInfo['compression'] = $this->get_xflag_type($thisInfo['raw']['xflags']); - $thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']); - if (!$thisThisFileInfo['os']) { - $ThisFileInfo['error'][] = 'Read error on gzip file'; + $thisInfo['os'] = $this->get_os_type($thisInfo['raw']['os']); + if (!$thisInfo['os']) { + $info['error'][] = 'Read error on gzip file'; return false; } @@ -102,12 +106,12 @@ class getid3_gzip { //+---+---+=================================+ //| XLEN |...XLEN bytes of "extra field"...| //+---+---+=================================+ - if ($thisThisFileInfo['flags']['extra']) { + if ($thisInfo['flags']['extra']) { $w_xlen = substr($buff, $fpointer, 2); $xlen = getid3_lib::LittleEndian2Int($w_xlen); $fpointer += 2; - $thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen); + $thisInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen); // Extra SubFields //+---+---+---+---+==================================+ //|SI1|SI2| LEN |... LEN bytes of subfield data ...| @@ -136,14 +140,14 @@ class getid3_gzip { //|...original file name, zero-terminated...| //+=========================================+ // GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz - $thisThisFileInfo['filename'] = preg_replace('#\.gz$#i', '', $ThisFileInfo['filename']); - if ($thisThisFileInfo['flags']['filename']) { + $thisInfo['filename'] = preg_replace('#\.gz$#i', '', $info['filename']); + if ($thisInfo['flags']['filename']) { while (true) { if (ord($buff[$fpointer]) == 0) { $fpointer++; break; } - $thisThisFileInfo['filename'] .= $buff[$fpointer]; + $thisInfo['filename'] .= $buff[$fpointer]; $fpointer++; } } @@ -151,13 +155,13 @@ class getid3_gzip { //+===================================+ //|...file comment, zero-terminated...| //+===================================+ - if ($thisThisFileInfo['flags']['comment']) { + if ($thisInfo['flags']['comment']) { while (true) { if (ord($buff[$fpointer]) == 0) { $fpointer++; break; } - $thisThisFileInfo['comment'] .= $buff[$fpointer]; + $thisInfo['comment'] .= $buff[$fpointer]; $fpointer++; } } @@ -165,21 +169,21 @@ class getid3_gzip { //+---+---+ //| CRC16 | //+---+---+ - if ($thisThisFileInfo['flags']['crc16']) { + if ($thisInfo['flags']['crc16']) { $w_crc = substr($buff, $fpointer, 2); - $thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc); + $thisInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc); $fpointer += 2; } // bit 0 - FLG.FTEXT - //if ($thisThisFileInfo['raw']['flags'] & 0x01) { + //if ($thisInfo['raw']['flags'] & 0x01) { // Ignored... //} // bits 5, 6, 7 - reserved - $thisThisFileInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4)); - $thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4)); + $thisInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4)); + $thisInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4)); - $ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize'])); + $info['gzip']['files'] = getid3_lib::array_merge_clobber($info['gzip']['files'], getid3_lib::CreateDeepArray($thisInfo['filename'], '/', $thisInfo['filesize'])); if ($this->option_gzip_parse_contents) { // Try to inflate GZip @@ -193,13 +197,13 @@ class getid3_gzip { $inflated = gzinflate($cdata); // Calculate CRC32 for inflated content - $thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']); + $thisInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisInfo['crc32']); // determine format $formattest = substr($inflated, 0, 32774); - $newgetID3 = new getID3(); - $determined_format = $newgetID3->GetFileFormat($formattest); - unset($newgetID3); + $getid3_temp = new getID3(); + $determined_format = $getid3_temp->GetFileFormat($formattest); + unset($getid3_temp); // file format is determined $determined_format['module'] = (isset($determined_format['module']) ? $determined_format['module'] : ''); @@ -209,20 +213,21 @@ class getid3_gzip { if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && include_once(GETID3_INCLUDEPATH.$determined_format['include'])) { if (($temp_tar_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) { // can't find anywhere to create a temp file, abort - $ThisFileInfo['error'][] = 'Unable to create temp file to parse TAR inside GZIP file'; + $info['error'][] = 'Unable to create temp file to parse TAR inside GZIP file'; break; } if ($fp_temp_tar = fopen($temp_tar_filename, 'w+b')) { fwrite($fp_temp_tar, $inflated); - rewind($fp_temp_tar); - $getid3_tar = new getid3_tar($fp_temp_tar, $dummy); - $ThisFileInfo['gzip']['member_header'][$idx]['tar'] = $dummy['tar']; - unset($dummy); - unset($getid3_tar); fclose($fp_temp_tar); + $getid3_temp = new getID3(); + $getid3_temp->openfile($temp_tar_filename); + $getid3_tar = new getid3_tar($getid3_temp); + $getid3_tar->Analyze(); + $info['gzip']['member_header'][$idx]['tar'] = $getid3_temp->info['tar']; + unset($getid3_temp, $getid3_tar); unlink($temp_tar_filename); } else { - $ThisFileInfo['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file'; + $info['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file'; break; } } diff --git a/modules/getid3/module.archive.rar.php b/modules/getid3/module.archive.rar.php index 27dcc13a..4f5d46f8 100644 --- a/modules/getid3/module.archive.rar.php +++ b/modules/getid3/module.archive.rar.php @@ -14,33 +14,34 @@ ///////////////////////////////////////////////////////////////// -class getid3_rar +class getid3_rar extends getid3_handler { var $option_use_rar_extension = false; - function getid3_rar(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'rar'; + $info['fileformat'] = 'rar'; if ($this->option_use_rar_extension === true) { if (function_exists('rar_open')) { - if ($rp = rar_open($ThisFileInfo['filenamepath'])) { - $ThisFileInfo['rar']['files'] = array(); + if ($rp = rar_open($info['filenamepath'])) { + $info['rar']['files'] = array(); $entries = rar_list($rp); foreach ($entries as $entry) { - $ThisFileInfo['rar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize())); + $info['rar']['files'] = getid3_lib::array_merge_clobber($info['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize())); } rar_close($rp); return true; } else { - $ThisFileInfo['error'][] = 'failed to rar_open('.$ThisFileInfo['filename'].')'; + $info['error'][] = 'failed to rar_open('.$info['filename'].')'; } } else { - $ThisFileInfo['error'][] = 'RAR support does not appear to be available in this PHP installation'; + $info['error'][] = 'RAR support does not appear to be available in this PHP installation'; } } else { - $ThisFileInfo['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)'; + $info['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)'; } return false; diff --git a/modules/getid3/module.archive.szip.php b/modules/getid3/module.archive.szip.php index 2513c85c..3be62532 100644 --- a/modules/getid3/module.archive.szip.php +++ b/modules/getid3/module.archive.szip.php @@ -14,36 +14,35 @@ ///////////////////////////////////////////////////////////////// -class getid3_szip +class getid3_szip extends getid3_handler { - function getid3_szip(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $SZIPHeader = fread($fd, 6); - if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") { - $ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"'; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $SZIPHeader = fread($this->getid3->fp, 6); + if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") { + $info['error'][] = 'Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"'; return false; } + $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)); - $ThisFileInfo['fileformat'] = 'szip'; - - $ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); - $ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); - - while (!feof($fd)) { - $NextBlockID = fread($fd, 2); + while (!feof($this->getid3->fp)) { + $NextBlockID = fread($this->getid3->fp, 2); switch ($NextBlockID) { case 'SZ': // Note that szip files can be concatenated, this has the same effect as // concatenating the files. this also means that global header blocks // might be present between directory/data blocks. - fseek($fd, 4, SEEK_CUR); + fseek($this->getid3->fp, 4, SEEK_CUR); break; case 'BH': - $BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3)); - $BHheaderdata = fread($fd, $BHheaderbytes); + $BHheaderbytes = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 3)); + $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes); $BHheaderoffset = 0; while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) { //filename as \0 terminated string (empty string indicates end) @@ -79,7 +78,7 @@ class getid3_szip $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; - $ThisFileInfo['szip']['BH'][] = $BHdataArray; + $info['szip']['BH'][] = $BHdataArray; } break; diff --git a/modules/getid3/module.archive.tar.php b/modules/getid3/module.archive.tar.php index fafbbcb7..94d32039 100644 --- a/modules/getid3/module.archive.tar.php +++ b/modules/getid3/module.archive.tar.php @@ -19,21 +19,21 @@ ///////////////////////////////////////////////////////////////// -class getid3_tar { +class getid3_tar extends getid3_handler +{ - function getid3_tar(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'tar'; - $ThisFileInfo['tar']['files'] = array(); + function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'tar'; + $info['tar']['files'] = array(); $unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix'; $null_512k = str_repeat("\x00", 512); // end-of-file marker - ob_start(); - fseek($fd, 0); - $errormessage = ob_get_contents(); - ob_end_clean(); - while (!feof($fd)) { - $buffer = fread($fd, 512); + fseek($this->getid3->fp, 0); + while (!feof($this->getid3->fp)) { + $buffer = fread($this->getid3->fp, 512); if (strlen($buffer) < 512) { break; } @@ -82,18 +82,18 @@ class getid3_tar { } // Read to the next chunk - fseek($fd, $size, SEEK_CUR); + fseek($this->getid3->fp, $size, SEEK_CUR); $diff = $size % 512; if ($diff != 0) { // Padding, throw away - fseek($fd, (512 - $diff), SEEK_CUR); + fseek($this->getid3->fp, (512 - $diff), SEEK_CUR); } // Protect against tar-files with garbage at the end if ($name == '') { break; } - $ThisFileInfo['tar']['file_details'][$name] = array ( + $info['tar']['file_details'][$name] = array ( 'name' => $name, 'mode_raw' => $mode, 'mode' => getid3_tar::display_perms($mode), @@ -111,7 +111,7 @@ class getid3_tar { 'devmajor' => $devmaj, 'devminor' => $devmin ); - $ThisFileInfo['tar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['tar']['files'], getid3_lib::CreateDeepArray($ThisFileInfo['tar']['file_details'][$name]['name'], '/', $size)); + $info['tar']['files'] = getid3_lib::array_merge_clobber($info['tar']['files'], getid3_lib::CreateDeepArray($info['tar']['file_details'][$name]['name'], '/', $size)); } return true; } diff --git a/modules/getid3/module.archive.zip.php b/modules/getid3/module.archive.zip.php index 38bc199c..7db8fd23 100644 --- a/modules/getid3/module.archive.zip.php +++ b/modules/getid3/module.archive.zip.php @@ -14,66 +14,67 @@ ///////////////////////////////////////////////////////////////// -class getid3_zip +class getid3_zip extends getid3_handler { - function getid3_zip(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'zip'; - $ThisFileInfo['zip']['encoding'] = 'ISO-8859-1'; - $ThisFileInfo['zip']['files'] = array(); + $info['fileformat'] = 'zip'; + $info['zip']['encoding'] = 'ISO-8859-1'; + $info['zip']['files'] = array(); - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; + $info['zip']['compressed_size'] = 0; + $info['zip']['uncompressed_size'] = 0; + $info['zip']['entries_count'] = 0; - if (!getid3_lib::intValueSupported($ThisFileInfo['filesize'])) { - $ThisFileInfo['error'][] = 'File is larger than '.round(PHP_INT_MAX / 1073741824).'GB, not supported by PHP'; + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['error'][] = 'File is larger than '.round(PHP_INT_MAX / 1073741824).'GB, not supported by PHP'; return false; } else { $EOCDsearchData = ''; $EOCDsearchCounter = 0; while ($EOCDsearchCounter++ < 512) { - fseek($fd, -128 * $EOCDsearchCounter, SEEK_END); - $EOCDsearchData = fread($fd, 128).$EOCDsearchData; + fseek($this->getid3->fp, -128 * $EOCDsearchCounter, SEEK_END); + $EOCDsearchData = fread($this->getid3->fp, 128).$EOCDsearchData; if (strstr($EOCDsearchData, 'PK'."\x05\x06")) { $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06"); - fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); - $ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd); + fseek($this->getid3->fp, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); + $info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory(); - fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET); - $ThisFileInfo['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { - $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; + fseek($this->getid3->fp, $info['zip']['end_central_directory']['directory_offset'], SEEK_SET); + $info['zip']['entries_count'] = 0; + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { + $info['zip']['central_directory'][] = $centraldirectoryentry; + $info['zip']['entries_count']++; + $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; + $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; if ($centraldirectoryentry['uncompressed_size'] > 0) { - $ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); + $info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); } } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; + if ($info['zip']['entries_count'] == 0) { + $info['error'][] = 'No Central Directory entries found (truncated file?)'; return false; } - if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { - $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; + if (!empty($info['zip']['end_central_directory']['comment'])) { + $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment']; } - if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) { - $ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method']; + if (isset($info['zip']['central_directory'][0]['compression_method'])) { + $info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method']; } - if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) { - $ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed']; + if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) { + $info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed']; } - if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) { - $ThisFileInfo['zip']['compression_speed'] = 'store'; + if (isset($info['zip']['compression_method']) && ($info['zip']['compression_method'] == 'store') && !isset($info['zip']['compression_speed'])) { + $info['zip']['compression_speed'] = 'store'; } return true; @@ -82,88 +83,92 @@ class getid3_zip } } - if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) { + if ($this->getZIPentriesFilepointer()) { // central directory couldn't be found and/or parsed // scan through actual file data entries, recover as much as possible from probable trucated file - if ($ThisFileInfo['zip']['compressed_size'] > ($ThisFileInfo['filesize'] - 46 - 22)) { - $ThisFileInfo['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$ThisFileInfo['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($ThisFileInfo['filesize'] - 46 - 22).' bytes)'; + if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) { + $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)'; } - $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'; - foreach ($ThisFileInfo['zip']['entries'] as $key => $valuearray) { - $ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size']; + $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'; + foreach ($info['zip']['entries'] as $key => $valuearray) { + $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size']; } return true; } else { - unset($ThisFileInfo['zip']); - $ThisFileInfo['fileformat'] = ''; - $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; + unset($info['zip']); + $info['fileformat'] = ''; + $info['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; return false; } } - function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'zip'; + function getZIPHeaderFilepointerTopDown() { + $info = &$this->getid3->info; - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; + $info['fileformat'] = 'zip'; - rewind($fd); - while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { - $ThisFileInfo['zip']['entries'][] = $fileentry; - $ThisFileInfo['zip']['entries_count']++; + $info['zip']['compressed_size'] = 0; + $info['zip']['uncompressed_size'] = 0; + $info['zip']['entries_count'] = 0; + + rewind($this->getid3->fp); + while ($fileentry = $this->ZIPparseLocalFileHeader()) { + $info['zip']['entries'][] = $fileentry; + $info['zip']['entries_count']++; } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Local File Header entries found'; + if ($info['zip']['entries_count'] == 0) { + $info['error'][] = 'No Local File Header entries found'; return false; } - $ThisFileInfo['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { - $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; + $info['zip']['entries_count'] = 0; + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { + $info['zip']['central_directory'][] = $centraldirectoryentry; + $info['zip']['entries_count']++; + $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; + $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; + if ($info['zip']['entries_count'] == 0) { + $info['error'][] = 'No Central Directory entries found (truncated file?)'; return false; } - if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) { - $ThisFileInfo['zip']['end_central_directory'] = $EOCD; + if ($EOCD = $this->ZIPparseEndOfCentralDirectory()) { + $info['zip']['end_central_directory'] = $EOCD; } else { - $ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)'; + $info['error'][] = 'No End Of Central Directory entry found (truncated file?)'; return false; } - if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { - $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; + if (!empty($info['zip']['end_central_directory']['comment'])) { + $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment']; } return true; } - function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) { - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; + function getZIPentriesFilepointer() { + $info = &$this->getid3->info; + + $info['zip']['compressed_size'] = 0; + $info['zip']['uncompressed_size'] = 0; + $info['zip']['entries_count'] = 0; - rewind($fd); - while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { - $ThisFileInfo['zip']['entries'][] = $fileentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size']; + rewind($this->getid3->fp); + while ($fileentry = $this->ZIPparseLocalFileHeader()) { + $info['zip']['entries'][] = $fileentry; + $info['zip']['entries_count']++; + $info['zip']['compressed_size'] += $fileentry['compressed_size']; + $info['zip']['uncompressed_size'] += $fileentry['uncompressed_size']; } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Local File Header entries found'; + if ($info['zip']['entries_count'] == 0) { + $info['error'][] = 'No Local File Header entries found'; return false; } @@ -171,15 +176,15 @@ class getid3_zip } - function ZIPparseLocalFileHeader(&$fd) { - $LocalFileHeader['offset'] = ftell($fd); + function ZIPparseLocalFileHeader() { + $LocalFileHeader['offset'] = ftell($this->getid3->fp); - $ZIPlocalFileHeader = fread($fd, 30); + $ZIPlocalFileHeader = fread($this->getid3->fp, 30); $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4)); if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // invalid Local File Header Signature - fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + fseek($this->getid3->fp, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly return false; } $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2)); @@ -203,7 +208,7 @@ class getid3_zip $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length']; if ($FilenameExtrafieldLength > 0) { - $ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength); + $ZIPlocalFileHeader .= fread($this->getid3->fp, $FilenameExtrafieldLength); if ($LocalFileHeader['raw']['filename_length'] > 0) { $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']); @@ -213,12 +218,12 @@ class getid3_zip } } - $LocalFileHeader['data_offset'] = ftell($fd); - //$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']); - fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR); + $LocalFileHeader['data_offset'] = ftell($this->getid3->fp); + //$LocalFileHeader['compressed_data'] = fread($this->getid3->fp, $LocalFileHeader['raw']['compressed_size']); + fseek($this->getid3->fp, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR); if ($LocalFileHeader['flags']['data_descriptor_used']) { - $DataDescriptor = fread($fd, 12); + $DataDescriptor = fread($this->getid3->fp, 12); $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4)); $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4)); @@ -228,15 +233,15 @@ class getid3_zip } - function ZIPparseCentralDirectory(&$fd) { - $CentralDirectory['offset'] = ftell($fd); + function ZIPparseCentralDirectory() { + $CentralDirectory['offset'] = ftell($this->getid3->fp); - $ZIPcentralDirectory = fread($fd, 46); + $ZIPcentralDirectory = fread($this->getid3->fp, 46); $CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4)); if ($CentralDirectory['raw']['signature'] != 0x02014B50) { // invalid Central Directory Signature - fseek($fd, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + fseek($this->getid3->fp, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly return false; } $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2)); @@ -268,7 +273,7 @@ class getid3_zip $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length']; if ($FilenameExtrafieldCommentLength > 0) { - $FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength); + $FilenameExtrafieldComment = fread($this->getid3->fp, $FilenameExtrafieldCommentLength); if ($CentralDirectory['raw']['filename_length'] > 0) { $CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']); @@ -284,15 +289,15 @@ class getid3_zip return $CentralDirectory; } - function ZIPparseEndOfCentralDirectory(&$fd) { - $EndOfCentralDirectory['offset'] = ftell($fd); + function ZIPparseEndOfCentralDirectory() { + $EndOfCentralDirectory['offset'] = ftell($this->getid3->fp); - $ZIPendOfCentralDirectory = fread($fd, 22); + $ZIPendOfCentralDirectory = fread($this->getid3->fp, 22); $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); if ($EndOfCentralDirectory['signature'] != 0x06054B50) { // invalid End Of Central Directory Signature - fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + fseek($this->getid3->fp, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly return false; } $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); @@ -304,14 +309,14 @@ class getid3_zip $EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); if ($EndOfCentralDirectory['comment_length'] > 0) { - $EndOfCentralDirectory['comment'] = fread($fd, $EndOfCentralDirectory['comment_length']); + $EndOfCentralDirectory['comment'] = fread($this->getid3->fp, $EndOfCentralDirectory['comment_length']); } return $EndOfCentralDirectory; } - function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { + static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); switch ($compressionmethod) { @@ -344,7 +349,7 @@ class getid3_zip } - function ZIPversionOSLookup($index) { + static function ZIPversionOSLookup($index) { static $ZIPversionOSLookup = array( 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', 1 => 'Amiga', @@ -369,7 +374,7 @@ class getid3_zip return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); } - function ZIPcompressionMethodLookup($index) { + static function ZIPcompressionMethodLookup($index) { static $ZIPcompressionMethodLookup = array( 0 => 'store', 1 => 'shrink', @@ -387,7 +392,7 @@ class getid3_zip return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); } - function DOStime2UNIXtime($DOSdate, $DOStime) { + static function DOStime2UNIXtime($DOSdate, $DOStime) { // wFatDate // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: // Bits Contents diff --git a/modules/getid3/module.audio-video.asf.php b/modules/getid3/module.audio-video.asf.php index ca9ace0e..416417c8 100644 --- a/modules/getid3/module.audio-video.asf.php +++ b/modules/getid3/module.audio-video.asf.php @@ -15,24 +15,29 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -$GUIDarray = getid3_asf::KnownGUIDs(); -foreach ($GUIDarray as $GUIDname => $hexstringvalue) { - // initialize all GUID constants - define($GUIDname, getid3_asf::GUIDtoBytestring($hexstringvalue)); -} - +class getid3_asf extends getid3_handler +{ + function __construct(getID3 $getid3) { + parent::__construct($getid3); // extends getid3_handler::__construct() -class getid3_asf -{ + // initialize all GUID constants + $GUIDarray = $this->KnownGUIDs(); + foreach ($GUIDarray as $GUIDname => $hexstringvalue) { + if (!defined($GUIDname)) { + define($GUIDname, $this->GUIDtoBytestring($hexstringvalue)); + } + } + } - function getid3_asf(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // Shortcuts - $thisfile_audio = &$ThisFileInfo['audio']; - $thisfile_video = &$ThisFileInfo['video']; - $ThisFileInfo['asf'] = array(); - $thisfile_asf = &$ThisFileInfo['asf']; + $thisfile_audio = &$info['audio']; + $thisfile_video = &$info['video']; + $info['asf'] = array(); + $thisfile_asf = &$info['asf']; $thisfile_asf['comments'] = array(); $thisfile_asf_comments = &$thisfile_asf['comments']; $thisfile_asf['header_object'] = array(); @@ -59,17 +64,17 @@ class getid3_asf // Reserved1 BYTE 8 // hardcoded: 0x01 // Reserved2 BYTE 8 // hardcoded: 0x02 - $ThisFileInfo['fileformat'] = 'asf'; + $info['fileformat'] = 'asf'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $HeaderObjectData = fread($fd, 30); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $HeaderObjectData = fread($this->getid3->fp, 30); $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { - $ThisFileInfo['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['asf']); + $info['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; + unset($info['fileformat']); + unset($info['asf']); return false; break; } @@ -78,11 +83,12 @@ class getid3_asf $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); - $ASFHeaderData = fread($fd, $thisfile_asf_headerobject['objectsize'] - 30); + $NextObjectOffset = ftell($this->getid3->fp); + $ASFHeaderData = fread($this->getid3->fp, $thisfile_asf_headerobject['objectsize'] - 30); $offset = 0; for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { - $NextObjectGUID = substr($ASFHeaderData, $offset, 16); + $NextObjectGUID = substr($ASFHeaderData, $offset, 16); $offset += 16; $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); $NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); @@ -113,6 +119,7 @@ class getid3_asf $thisfile_asf['file_properties_object'] = array(); $thisfile_asf_filepropertiesobject = &$thisfile_asf['file_properties_object']; + $thisfile_asf_filepropertiesobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_filepropertiesobject['objectid'] = $NextObjectGUID; $thisfile_asf_filepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_filepropertiesobject['objectsize'] = $NextObjectSize; @@ -157,10 +164,10 @@ class getid3_asf } else { // broadcast flag NOT set, perform calculations - $ThisFileInfo['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000); + $info['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000); - //$ThisFileInfo['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate']; - $ThisFileInfo['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $ThisFileInfo['filesize']) * 8) / $ThisFileInfo['playtime_seconds']; + //$info['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate']; + $info['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $info['filesize']) * 8) / $info['playtime_seconds']; } break; @@ -186,6 +193,7 @@ class getid3_asf // stream number isn't known until halfway through decoding the structure, hence it // it is decoded to a temporary variable and then stuck in the appropriate index later + $StreamPropertiesObjectData['offset'] = $NextObjectOffset + $offset; $StreamPropertiesObjectData['objectid'] = $NextObjectGUID; $StreamPropertiesObjectData['objectid_guid'] = $NextObjectGUIDtext; $StreamPropertiesObjectData['objectsize'] = $NextObjectSize; @@ -253,6 +261,7 @@ class getid3_asf $thisfile_asf['header_extension_object'] = array(); $thisfile_asf_headerextensionobject = &$thisfile_asf['header_extension_object']; + $thisfile_asf_headerextensionobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_headerextensionobject['objectid'] = $NextObjectGUID; $thisfile_asf_headerextensionobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_headerextensionobject['objectsize'] = $NextObjectSize; @@ -260,20 +269,25 @@ class getid3_asf $offset += 16; $thisfile_asf_headerextensionobject['reserved_1_guid'] = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']); if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) { - $ThisFileInfo['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')'; + $info['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')'; //return false; break; } $thisfile_asf_headerextensionobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) { - $ThisFileInfo['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"'; + $info['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"'; //return false; break; } $thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); $offset += 4; $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); + $unhandled_sections = 0; + $thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->ASF_HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections); + if ($unhandled_sections === 0) { + unset($thisfile_asf_headerextensionobject['extension_data']); + } $offset += $thisfile_asf_headerextensionobject['extension_data_size']; break; @@ -297,6 +311,7 @@ class getid3_asf $thisfile_asf['codec_list_object'] = array(); $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; + $thisfile_asf_codeclistobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_codeclistobject['objectid'] = $NextObjectGUID; $thisfile_asf_codeclistobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_codeclistobject['objectsize'] = $NextObjectSize; @@ -304,7 +319,7 @@ class getid3_asf $offset += 16; $thisfile_asf_codeclistobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']); if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) { - $ThisFileInfo['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}'; + $info['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}'; //return false; break; } @@ -337,7 +352,7 @@ class getid3_asf if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { // audio codec if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) { - $ThisFileInfo['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"'; + $info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"'; } else { list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description'])); @@ -347,9 +362,9 @@ class getid3_asf $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000); } //if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) { - if (empty($thisfile_video['bitrate']) && !empty($thisfile_audio['bitrate']) && !empty($ThisFileInfo['bitrate'])) { + if (empty($thisfile_video['bitrate']) && !empty($thisfile_audio['bitrate']) && !empty($info['bitrate'])) { //$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate']; - $thisfile_video['bitrate'] = $ThisFileInfo['bitrate'] - $thisfile_audio['bitrate']; + $thisfile_video['bitrate'] = $info['bitrate'] - $thisfile_audio['bitrate']; } $AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency)); @@ -400,7 +415,7 @@ class getid3_asf break; default: - $ThisFileInfo['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')'; + $info['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')'; break; } @@ -438,6 +453,7 @@ class getid3_asf $thisfile_asf['script_command_object'] = array(); $thisfile_asf_scriptcommandobject = &$thisfile_asf['script_command_object']; + $thisfile_asf_scriptcommandobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_scriptcommandobject['objectid'] = $NextObjectGUID; $thisfile_asf_scriptcommandobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_scriptcommandobject['objectsize'] = $NextObjectSize; @@ -445,7 +461,7 @@ class getid3_asf $offset += 16; $thisfile_asf_scriptcommandobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']); if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) { - $ThisFileInfo['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}'; + $info['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}'; //return false; break; } @@ -496,6 +512,7 @@ class getid3_asf $thisfile_asf['marker_object'] = array(); $thisfile_asf_markerobject = &$thisfile_asf['marker_object']; + $thisfile_asf_markerobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_markerobject['objectid'] = $NextObjectGUID; $thisfile_asf_markerobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_markerobject['objectsize'] = $NextObjectSize; @@ -503,7 +520,7 @@ class getid3_asf $offset += 16; $thisfile_asf_markerobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']); if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) { - $ThisFileInfo['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}'; + $info['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}'; break; } $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); @@ -511,7 +528,7 @@ class getid3_asf $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; if ($thisfile_asf_markerobject['reserved_2'] != 0) { - $ThisFileInfo['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"'; + $info['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"'; break; } $thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); @@ -554,6 +571,7 @@ class getid3_asf $thisfile_asf['bitrate_mutual_exclusion_object'] = array(); $thisfile_asf_bitratemutualexclusionobject = &$thisfile_asf['bitrate_mutual_exclusion_object']; + $thisfile_asf_bitratemutualexclusionobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_bitratemutualexclusionobject['objectid'] = $NextObjectGUID; $thisfile_asf_bitratemutualexclusionobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_bitratemutualexclusionobject['objectsize'] = $NextObjectSize; @@ -561,7 +579,7 @@ class getid3_asf $thisfile_asf_bitratemutualexclusionobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']); $offset += 16; if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) { - $ThisFileInfo['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}'; + $info['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}'; //return false; break; } @@ -586,6 +604,7 @@ class getid3_asf $thisfile_asf['error_correction_object'] = array(); $thisfile_asf_errorcorrectionobject = &$thisfile_asf['error_correction_object']; + $thisfile_asf_errorcorrectionobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_errorcorrectionobject['objectid'] = $NextObjectGUID; $thisfile_asf_errorcorrectionobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_errorcorrectionobject['objectsize'] = $NextObjectSize; @@ -621,7 +640,7 @@ class getid3_asf break; default: - $ThisFileInfo['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}'; + $info['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}'; //return false; break; } @@ -648,6 +667,7 @@ class getid3_asf $thisfile_asf['content_description_object'] = array(); $thisfile_asf_contentdescriptionobject = &$thisfile_asf['content_description_object']; + $thisfile_asf_contentdescriptionobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_contentdescriptionobject['objectid'] = $NextObjectGUID; $thisfile_asf_contentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_contentdescriptionobject['objectsize'] = $NextObjectSize; @@ -703,6 +723,7 @@ class getid3_asf $thisfile_asf['extended_content_description_object'] = array(); $thisfile_asf_extendedcontentdescriptionobject = &$thisfile_asf['extended_content_description_object']; + $thisfile_asf_extendedcontentdescriptionobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_extendedcontentdescriptionobject['objectid'] = $NextObjectGUID; $thisfile_asf_extendedcontentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_extendedcontentdescriptionobject['objectsize'] = $NextObjectSize; @@ -711,7 +732,7 @@ class getid3_asf for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { // shortcut $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array(); - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset'] = $offset + 30; $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); @@ -743,7 +764,7 @@ class getid3_asf break; default: - $ThisFileInfo['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')'; + $info['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')'; //return false; break; } @@ -808,19 +829,19 @@ class getid3_asf // id3v2 module might not be loaded if (class_exists('getid3_id3v2')) { $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3'); - $tempfilehandle = fopen($tempfile, "wb"); - $tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']); + $tempfilehandle = fopen($tempfile, 'wb'); + $tempThisfileInfo = array('encoding'=>$info['encoding']); fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); fclose($tempfilehandle); - $tempfilehandle = fopen($tempfile, "rb"); - $id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo); - unset($id3); - fclose($tempfilehandle); - unlink($tempfile); + $getid3_temp = new getID3(); + $getid3_temp->openfile($tempfile); + $getid3_id3v2 = new getid3_id3v2($getid3_temp); + $getid3_id3v2->Analyze(); + $info['id3v2'] = $getid3_temp->info['id3v2']; + unset($getid3_temp, $getid3_id3v2); - $ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2']; - unset($tempThisfileInfo); + unlink($tempfile); } break; @@ -830,14 +851,12 @@ class getid3_asf break; case 'wm/picture': - //typedef struct _WMPicture{ - // LPWSTR pwszMIMEType; - // BYTE bPictureType; - // LPWSTR pwszDescription; - // DWORD dwDataLen; - // BYTE* pbData; - //} WM_PICTURE; - + $WMpicture = $this->ASF_WMpicture($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + foreach ($WMpicture as $key => $value) { + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current[$key] = $value; + } + unset($WMpicture); +/* $wm_picture_offset = 0; $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); $wm_picture_offset += 1; @@ -873,7 +892,8 @@ class getid3_asf if (!isset($thisfile_asf_comments['picture'])) { $thisfile_asf_comments['picture'] = array(); } - $thisfile_asf_comments['picture'][] = array('data'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'], 'mime_type'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime']); + $thisfile_asf_comments['picture'][] = array('data'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'], 'image_mime'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime']); +*/ break; default: @@ -909,6 +929,7 @@ class getid3_asf $thisfile_asf['stream_bitrate_properties_object'] = array(); $thisfile_asf_streambitratepropertiesobject = &$thisfile_asf['stream_bitrate_properties_object']; + $thisfile_asf_streambitratepropertiesobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_streambitratepropertiesobject['objectid'] = $NextObjectGUID; $thisfile_asf_streambitratepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_streambitratepropertiesobject['objectsize'] = $NextObjectSize; @@ -934,6 +955,7 @@ class getid3_asf $thisfile_asf['padding_object'] = array(); $thisfile_asf_paddingobject = &$thisfile_asf['padding_object']; + $thisfile_asf_paddingobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_paddingobject['objectid'] = $NextObjectGUID; $thisfile_asf_paddingobject['objectid_guid'] = $NextObjectGUIDtext; $thisfile_asf_paddingobject['objectsize'] = $NextObjectSize; @@ -951,9 +973,9 @@ class getid3_asf default: // Implementations shall ignore any standard or non-standard object that they do not know how to handle. if ($this->GUIDname($NextObjectGUIDtext)) { - $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); } else { - $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); } $offset += ($NextObjectSize - 16 - 8); break; @@ -1134,8 +1156,8 @@ class getid3_asf } } - while (ftell($fd) < $ThisFileInfo['avdataend']) { - $NextObjectDataHeader = fread($fd, 24); + while (ftell($this->getid3->fp) < $info['avdataend']) { + $NextObjectDataHeader = fread($this->getid3->fp, 24); $offset = 0; $NextObjectGUID = substr($NextObjectDataHeader, 0, 16); $offset += 16; @@ -1157,7 +1179,7 @@ class getid3_asf $thisfile_asf['data_object'] = array(); $thisfile_asf_dataobject = &$thisfile_asf['data_object']; - $DataObjectData = $NextObjectDataHeader.fread($fd, 50 - 24); + $DataObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 50 - 24); $offset = 24; $thisfile_asf_dataobject['objectid'] = $NextObjectGUID; @@ -1172,7 +1194,7 @@ class getid3_asf $thisfile_asf_dataobject['reserved'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 2)); $offset += 2; if ($thisfile_asf_dataobject['reserved'] != 0x0101) { - $ThisFileInfo['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"'; + $info['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"'; //return false; break; } @@ -1185,9 +1207,9 @@ class getid3_asf // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure // * Error Correction Data - $ThisFileInfo['avdataoffset'] = ftell($fd); - fseek($fd, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data - $ThisFileInfo['avdataend'] = ftell($fd); + $info['avdataoffset'] = ftell($this->getid3->fp); + fseek($this->getid3->fp, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data + $info['avdataend'] = ftell($this->getid3->fp); break; case GETID3_ASF_Simple_Index_Object: @@ -1207,7 +1229,7 @@ class getid3_asf $thisfile_asf['simple_index_object'] = array(); $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; - $SimpleIndexObjectData = $NextObjectDataHeader.fread($fd, 56 - 24); + $SimpleIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 56 - 24); $offset = 24; $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; @@ -1224,7 +1246,7 @@ class getid3_asf $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); $offset += 4; - $IndexEntriesData = $SimpleIndexObjectData.fread($fd, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); + $IndexEntriesData = $SimpleIndexObjectData.fread($this->getid3->fp, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); $offset += 4; @@ -1261,7 +1283,7 @@ class getid3_asf $thisfile_asf['asf_index_object'] = array(); $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; - $ASFIndexObjectData = $NextObjectDataHeader.fread($fd, 34 - 24); + $ASFIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 34 - 24); $offset = 24; $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; @@ -1275,7 +1297,7 @@ class getid3_asf $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $offset += 4; - $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); + $ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); $offset += 2; @@ -1285,17 +1307,17 @@ class getid3_asf $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); } - $ASFIndexObjectData .= fread($fd, 4); + $ASFIndexObjectData .= fread($this->getid3->fp, 4); $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $offset += 4; - $ASFIndexObjectData .= fread($fd, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); + $ASFIndexObjectData .= fread($this->getid3->fp, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); $offset += 8; } - $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); + $ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); @@ -1308,11 +1330,11 @@ class getid3_asf default: // Implementations shall ignore any standard or non-standard object that they do not know how to handle. if ($this->GUIDname($NextObjectGUIDtext)) { - $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); + $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); } else { - $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($fd) - 16 - 8); + $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($this->getid3->fp) - 16 - 8); } - fseek($fd, ($NextObjectSize - 16 - 8), SEEK_CUR); + fseek($this->getid3->fp, ($NextObjectSize - 16 - 8), SEEK_CUR); break; } } @@ -1330,7 +1352,7 @@ class getid3_asf case 'WMVP': case 'WVP2': $thisfile_video['dataformat'] = 'wmv'; - $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; + $info['mime_type'] = 'video/x-ms-wmv'; break; case 'MP42': @@ -1338,7 +1360,7 @@ class getid3_asf case 'MP4S': case 'mp4s': $thisfile_video['dataformat'] = 'asf'; - $ThisFileInfo['mime_type'] = 'video/x-ms-asf'; + $info['mime_type'] = 'video/x-ms-asf'; break; default: @@ -1346,8 +1368,8 @@ class getid3_asf case 1: if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { $thisfile_video['dataformat'] = 'wmv'; - if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { - $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; + if ($info['mime_type'] == 'video/x-ms-asf') { + $info['mime_type'] = 'video/x-ms-wmv'; } } break; @@ -1355,8 +1377,8 @@ class getid3_asf case 2: if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { $thisfile_audio['dataformat'] = 'wma'; - if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { - $ThisFileInfo['mime_type'] = 'audio/x-ms-wma'; + if ($info['mime_type'] == 'video/x-ms-asf') { + $info['mime_type'] = 'audio/x-ms-wma'; } } break; @@ -1394,14 +1416,14 @@ class getid3_asf break; default: - $ThisFileInfo['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']; + $info['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']; break; } } } - if (isset($ThisFileInfo['audio'])) { + if (isset($info['audio'])) { $thisfile_audio['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); } @@ -1420,10 +1442,10 @@ class getid3_asf } } } - $ThisFileInfo['bitrate'] = (isset($thisfile_audio['bitrate']) ? $thisfile_audio['bitrate'] : 0) + (isset($thisfile_video['bitrate']) ? $thisfile_video['bitrate'] : 0); + $info['bitrate'] = (isset($thisfile_audio['bitrate']) ? $thisfile_audio['bitrate'] : 0) + (isset($thisfile_video['bitrate']) ? $thisfile_video['bitrate'] : 0); - if ((!isset($ThisFileInfo['playtime_seconds']) || ($ThisFileInfo['playtime_seconds'] <= 0)) && ($ThisFileInfo['bitrate'] > 0)) { - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['bitrate'] / 8); + if ((!isset($info['playtime_seconds']) || ($info['playtime_seconds'] <= 0)) && ($info['bitrate'] > 0)) { + $info['playtime_seconds'] = ($info['filesize'] - $info['avdataoffset']) / ($info['bitrate'] / 8); } return true; @@ -1441,115 +1463,116 @@ class getid3_asf } static function KnownGUIDs() { - static $GUIDarray = array(); - if (empty($GUIDarray)) { - $GUIDarray['GETID3_ASF_Extended_Stream_Properties_Object'] = '14E6A5CB-C672-4332-8399-A96952065B5A'; - $GUIDarray['GETID3_ASF_Padding_Object'] = '1806D474-CADF-4509-A4BA-9AABCB96AAE8'; - $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio'] = '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8'; - $GUIDarray['GETID3_ASF_Script_Command_Object'] = '1EFB1A30-0B62-11D0-A39B-00A0C90348F6'; - $GUIDarray['GETID3_ASF_No_Error_Correction'] = '20FB5700-5B55-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Content_Branding_Object'] = '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Content_Encryption_Object'] = '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Digital_Signature_Object'] = '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Extended_Content_Encryption_Object'] = '298AE614-2622-4C17-B935-DAE07EE9289C'; - $GUIDarray['GETID3_ASF_Simple_Index_Object'] = '33000890-E5B1-11CF-89F4-00A0C90349CB'; - $GUIDarray['GETID3_ASF_Degradable_JPEG_Media'] = '35907DE0-E415-11CF-A917-00805F5C442B'; - $GUIDarray['GETID3_ASF_Payload_Extension_System_Timecode'] = '399595EC-8667-4E2D-8FDB-98814CE76C1E'; - $GUIDarray['GETID3_ASF_Binary_Media'] = '3AFB65E2-47EF-40F2-AC2C-70A90D71D343'; - $GUIDarray['GETID3_ASF_Timecode_Index_Object'] = '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C'; - $GUIDarray['GETID3_ASF_Metadata_Library_Object'] = '44231C94-9498-49D1-A141-1D134E457054'; - $GUIDarray['GETID3_ASF_Reserved_3'] = '4B1ACBE3-100B-11D0-A39B-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Reserved_4'] = '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB'; - $GUIDarray['GETID3_ASF_Command_Media'] = '59DACFC0-59E6-11D0-A3AC-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Header_Extension_Object'] = '5FBF03B5-A92E-11CF-8EE3-00C00C205365'; - $GUIDarray['GETID3_ASF_Media_Object_Index_Parameters_Obj'] = '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7'; - $GUIDarray['GETID3_ASF_Header_Object'] = '75B22630-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Content_Description_Object'] = '75B22633-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Error_Correction_Object'] = '75B22635-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Data_Object'] = '75B22636-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Web_Stream_Media_Subtype'] = '776257D4-C627-41CB-8F81-7AC7FF1C40CC'; - $GUIDarray['GETID3_ASF_Stream_Bitrate_Properties_Object'] = '7BF875CE-468D-11D1-8D82-006097C9A2B2'; - $GUIDarray['GETID3_ASF_Language_List_Object'] = '7C4346A9-EFE0-4BFC-B229-393EDE415C85'; - $GUIDarray['GETID3_ASF_Codec_List_Object'] = '86D15240-311D-11D0-A3A4-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Reserved_2'] = '86D15241-311D-11D0-A3A4-00A0C90348F6'; - $GUIDarray['GETID3_ASF_File_Properties_Object'] = '8CABDCA1-A947-11CF-8EE4-00C00C205365'; - $GUIDarray['GETID3_ASF_File_Transfer_Media'] = '91BD222C-F21C-497A-8B6D-5AA86BFC0185'; - $GUIDarray['GETID3_ASF_Old_RTP_Extension_Data'] = '96800C63-4C94-11D1-837B-0080C7A37F95'; - $GUIDarray['GETID3_ASF_Advanced_Mutual_Exclusion_Object'] = 'A08649CF-4775-4670-8A16-6E35357566CD'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Object'] = 'A69609E6-517B-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_Reserved_1'] = 'ABD3D211-A9BA-11cf-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Exclusive'] = 'AF6060AA-5197-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Partial'] = 'AF6060AB-5197-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_JFIF_Media'] = 'B61BE100-5B4E-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Stream_Properties_Object'] = 'B7DC0791-A9B7-11CF-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Video_Media'] = 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Audio_Spread'] = 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220'; - $GUIDarray['GETID3_ASF_Metadata_Object'] = 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA'; - $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Sample_Duration'] = 'C6BD9450-867F-4907-83A3-C77921B733AD'; - $GUIDarray['GETID3_ASF_Group_Mutual_Exclusion_Object'] = 'D1465A40-5A79-4338-B71B-E36B8FD6C249'; - $GUIDarray['GETID3_ASF_Extended_Content_Description_Object'] = 'D2D0A440-E307-11D2-97F0-00A0C95EA850'; - $GUIDarray['GETID3_ASF_Stream_Prioritization_Object'] = 'D4FED15B-88D3-454F-81F0-ED5C45999E24'; - $GUIDarray['GETID3_ASF_Payload_Ext_System_Content_Type'] = 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC'; - $GUIDarray['GETID3_ASF_Old_File_Properties_Object'] = 'D6E229D0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Header_Object'] = 'D6E229D1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Data_Object'] = 'D6E229D2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Index_Object'] = 'D6E229D3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Stream_Properties_Object'] = 'D6E229D4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Content_Description_Object'] = 'D6E229D5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Script_Command_Object'] = 'D6E229D6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Marker_Object'] = 'D6E229D7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Component_Download_Object'] = 'D6E229D8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Stream_Group_Object'] = 'D6E229D9-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Scalable_Object'] = 'D6E229DA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Prioritization_Object'] = 'D6E229DB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Bitrate_Mutual_Exclusion_Object'] = 'D6E229DC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Inter_Media_Dependency_Object'] = 'D6E229DD-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Rating_Object'] = 'D6E229DE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Index_Parameters_Object'] = 'D6E229DF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Color_Table_Object'] = 'D6E229E0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Language_List_Object'] = 'D6E229E1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Audio_Media'] = 'D6E229E2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Video_Media'] = 'D6E229E3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Image_Media'] = 'D6E229E4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Timecode_Media'] = 'D6E229E5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Text_Media'] = 'D6E229E6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_MIDI_Media'] = 'D6E229E7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Command_Media'] = 'D6E229E8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Error_Concealment'] = 'D6E229EA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Scrambled_Audio'] = 'D6E229EB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Color_Table'] = 'D6E229EC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_SMPTE_Time'] = 'D6E229ED-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASCII_Text'] = 'D6E229EE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Unicode_Text'] = 'D6E229EF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_HTML_Text'] = 'D6E229F0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_URL_Command'] = 'D6E229F1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Filename_Command'] = 'D6E229F2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ACM_Codec'] = 'D6E229F3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_VCM_Codec'] = 'D6E229F4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_QuickTime_Codec'] = 'D6E229F5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_DirectShow_Transform_Filter'] = 'D6E229F6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_DirectShow_Rendering_Filter'] = 'D6E229F7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Enhancement'] = 'D6E229F8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Unknown_Enhancement_Type'] = 'D6E229F9-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Temporal_Enhancement'] = 'D6E229FA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Spatial_Enhancement'] = 'D6E229FB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Quality_Enhancement'] = 'D6E229FC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Number_of_Channels_Enhancement'] = 'D6E229FD-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Frequency_Response_Enhancement'] = 'D6E229FE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Media_Object'] = 'D6E229FF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Language'] = 'D6E22A00-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Bitrate'] = 'D6E22A01-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Unknown'] = 'D6E22A02-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Placeholder_Object'] = 'D6E22A0E-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Data_Unit_Extension_Object'] = 'D6E22A0F-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Web_Stream_Format'] = 'DA1E6B13-8359-4050-B398-388E965BF00C'; - $GUIDarray['GETID3_ASF_Payload_Ext_System_File_Name'] = 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B'; - $GUIDarray['GETID3_ASF_Marker_Object'] = 'F487CD01-A951-11CF-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Timecode_Index_Parameters_Object'] = 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24'; - $GUIDarray['GETID3_ASF_Audio_Media'] = 'F8699E40-5B4D-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Media_Object_Index_Object'] = 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C'; - $GUIDarray['GETID3_ASF_Alt_Extended_Content_Encryption_Obj'] = 'FF889EF1-ADEE-40DA-9E71-98704BB928CE'; - } + static $GUIDarray = array( + 'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A', + 'GETID3_ASF_Padding_Object' => '1806D474-CADF-4509-A4BA-9AABCB96AAE8', + 'GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio' => '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8', + 'GETID3_ASF_Script_Command_Object' => '1EFB1A30-0B62-11D0-A39B-00A0C90348F6', + 'GETID3_ASF_No_Error_Correction' => '20FB5700-5B55-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Content_Branding_Object' => '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Content_Encryption_Object' => '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Digital_Signature_Object' => '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Extended_Content_Encryption_Object' => '298AE614-2622-4C17-B935-DAE07EE9289C', + 'GETID3_ASF_Simple_Index_Object' => '33000890-E5B1-11CF-89F4-00A0C90349CB', + 'GETID3_ASF_Degradable_JPEG_Media' => '35907DE0-E415-11CF-A917-00805F5C442B', + 'GETID3_ASF_Payload_Extension_System_Timecode' => '399595EC-8667-4E2D-8FDB-98814CE76C1E', + 'GETID3_ASF_Binary_Media' => '3AFB65E2-47EF-40F2-AC2C-70A90D71D343', + 'GETID3_ASF_Timecode_Index_Object' => '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C', + 'GETID3_ASF_Metadata_Library_Object' => '44231C94-9498-49D1-A141-1D134E457054', + 'GETID3_ASF_Reserved_3' => '4B1ACBE3-100B-11D0-A39B-00A0C90348F6', + 'GETID3_ASF_Reserved_4' => '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB', + 'GETID3_ASF_Command_Media' => '59DACFC0-59E6-11D0-A3AC-00A0C90348F6', + 'GETID3_ASF_Header_Extension_Object' => '5FBF03B5-A92E-11CF-8EE3-00C00C205365', + 'GETID3_ASF_Media_Object_Index_Parameters_Obj' => '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7', + 'GETID3_ASF_Header_Object' => '75B22630-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Content_Description_Object' => '75B22633-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Error_Correction_Object' => '75B22635-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Data_Object' => '75B22636-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Web_Stream_Media_Subtype' => '776257D4-C627-41CB-8F81-7AC7FF1C40CC', + 'GETID3_ASF_Stream_Bitrate_Properties_Object' => '7BF875CE-468D-11D1-8D82-006097C9A2B2', + 'GETID3_ASF_Language_List_Object' => '7C4346A9-EFE0-4BFC-B229-393EDE415C85', + 'GETID3_ASF_Codec_List_Object' => '86D15240-311D-11D0-A3A4-00A0C90348F6', + 'GETID3_ASF_Reserved_2' => '86D15241-311D-11D0-A3A4-00A0C90348F6', + 'GETID3_ASF_File_Properties_Object' => '8CABDCA1-A947-11CF-8EE4-00C00C205365', + 'GETID3_ASF_File_Transfer_Media' => '91BD222C-F21C-497A-8B6D-5AA86BFC0185', + 'GETID3_ASF_Old_RTP_Extension_Data' => '96800C63-4C94-11D1-837B-0080C7A37F95', + 'GETID3_ASF_Advanced_Mutual_Exclusion_Object' => 'A08649CF-4775-4670-8A16-6E35357566CD', + 'GETID3_ASF_Bandwidth_Sharing_Object' => 'A69609E6-517B-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_Reserved_1' => 'ABD3D211-A9BA-11cf-8EE6-00C00C205365', + 'GETID3_ASF_Bandwidth_Sharing_Exclusive' => 'AF6060AA-5197-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_Bandwidth_Sharing_Partial' => 'AF6060AB-5197-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_JFIF_Media' => 'B61BE100-5B4E-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Stream_Properties_Object' => 'B7DC0791-A9B7-11CF-8EE6-00C00C205365', + 'GETID3_ASF_Video_Media' => 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Audio_Spread' => 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220', + 'GETID3_ASF_Metadata_Object' => 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA', + 'GETID3_ASF_Payload_Ext_Syst_Sample_Duration' => 'C6BD9450-867F-4907-83A3-C77921B733AD', + 'GETID3_ASF_Group_Mutual_Exclusion_Object' => 'D1465A40-5A79-4338-B71B-E36B8FD6C249', + 'GETID3_ASF_Extended_Content_Description_Object' => 'D2D0A440-E307-11D2-97F0-00A0C95EA850', + 'GETID3_ASF_Stream_Prioritization_Object' => 'D4FED15B-88D3-454F-81F0-ED5C45999E24', + 'GETID3_ASF_Payload_Ext_System_Content_Type' => 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC', + 'GETID3_ASF_Old_File_Properties_Object' => 'D6E229D0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Header_Object' => 'D6E229D1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Data_Object' => 'D6E229D2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Index_Object' => 'D6E229D3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Stream_Properties_Object' => 'D6E229D4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Content_Description_Object' => 'D6E229D5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Script_Command_Object' => 'D6E229D6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Marker_Object' => 'D6E229D7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Component_Download_Object' => 'D6E229D8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Stream_Group_Object' => 'D6E229D9-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Scalable_Object' => 'D6E229DA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Prioritization_Object' => 'D6E229DB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Bitrate_Mutual_Exclusion_Object' => 'D6E229DC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Inter_Media_Dependency_Object' => 'D6E229DD-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Rating_Object' => 'D6E229DE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Index_Parameters_Object' => 'D6E229DF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Color_Table_Object' => 'D6E229E0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Language_List_Object' => 'D6E229E1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Audio_Media' => 'D6E229E2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Video_Media' => 'D6E229E3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Image_Media' => 'D6E229E4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Timecode_Media' => 'D6E229E5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Text_Media' => 'D6E229E6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_MIDI_Media' => 'D6E229E7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Command_Media' => 'D6E229E8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Error_Concealment' => 'D6E229EA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Scrambled_Audio' => 'D6E229EB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Color_Table' => 'D6E229EC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_SMPTE_Time' => 'D6E229ED-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASCII_Text' => 'D6E229EE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Unicode_Text' => 'D6E229EF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_HTML_Text' => 'D6E229F0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_URL_Command' => 'D6E229F1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Filename_Command' => 'D6E229F2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ACM_Codec' => 'D6E229F3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_VCM_Codec' => 'D6E229F4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_QuickTime_Codec' => 'D6E229F5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_DirectShow_Transform_Filter' => 'D6E229F6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_DirectShow_Rendering_Filter' => 'D6E229F7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Enhancement' => 'D6E229F8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Unknown_Enhancement_Type' => 'D6E229F9-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Temporal_Enhancement' => 'D6E229FA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Spatial_Enhancement' => 'D6E229FB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Quality_Enhancement' => 'D6E229FC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Number_of_Channels_Enhancement' => 'D6E229FD-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Frequency_Response_Enhancement' => 'D6E229FE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Media_Object' => 'D6E229FF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Language' => 'D6E22A00-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Bitrate' => 'D6E22A01-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Unknown' => 'D6E22A02-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Placeholder_Object' => 'D6E22A0E-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Data_Unit_Extension_Object' => 'D6E22A0F-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Web_Stream_Format' => 'DA1E6B13-8359-4050-B398-388E965BF00C', + 'GETID3_ASF_Payload_Ext_System_File_Name' => 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B', + 'GETID3_ASF_Marker_Object' => 'F487CD01-A951-11CF-8EE6-00C00C205365', + 'GETID3_ASF_Timecode_Index_Parameters_Object' => 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24', + 'GETID3_ASF_Audio_Media' => 'F8699E40-5B4D-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Media_Object_Index_Object' => 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C', + 'GETID3_ASF_Alt_Extended_Content_Encryption_Obj' => 'FF889EF1-ADEE-40DA-9E71-98704BB928CE', + 'GETID3_ASF_Index_Placeholder_Object' => 'D9AADE20-7C17-4F9C-BC28-8555DD98E2A2', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html + 'GETID3_ASF_Compatibility_Object' => '26F18B5D-4584-47EC-9F5F-0E651F0452C9', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html + );
return $GUIDarray; } @@ -1667,8 +1690,318 @@ class getid3_asf return (isset($WMpictureTypeLookup[$WMpictureType]) ? $WMpictureTypeLookup[$WMpictureType] : ''); } + function ASF_HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { + // http://msdn.microsoft.com/en-us/library/bb643323.aspx + + $offset = 0; + $objectOffset = 0; + $HeaderExtensionObjectParsed = array(); + while ($objectOffset < strlen($asf_header_extension_object_data)) { + $offset = $objectOffset; + $thisObject = array(); + + $thisObject['guid'] = substr($asf_header_extension_object_data, $offset, 16); + $offset += 16; + $thisObject['guid_text'] = $this->BytestringToGUID($thisObject['guid']); + $thisObject['guid_name'] = $this->GUIDname($thisObject['guid_text']); + + $thisObject['size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + if ($thisObject['size'] <= 0) { + break; + } + + switch ($thisObject['guid']) { + case GETID3_ASF_Extended_Stream_Properties_Object: + $thisObject['start_time'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + $thisObject['start_time_unix'] = $this->FILETIMEtoUNIXtime($thisObject['start_time']); + + $thisObject['end_time'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + $thisObject['end_time_unix'] = $this->FILETIMEtoUNIXtime($thisObject['end_time']); + + $thisObject['data_bitrate'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['buffer_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['initial_buffer_fullness'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_data_bitrate'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_buffer_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_initial_buffer_fullness'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['maximum_object_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['flags_raw'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + $thisObject['flags']['reliable'] = (bool) $thisObject['flags_raw'] & 0x00000001; + $thisObject['flags']['seekable'] = (bool) $thisObject['flags_raw'] & 0x00000002; + $thisObject['flags']['no_cleanpoints'] = (bool) $thisObject['flags_raw'] & 0x00000004; + $thisObject['flags']['resend_live_cleanpoints'] = (bool) $thisObject['flags_raw'] & 0x00000008; + + $thisObject['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['stream_language_id_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['average_time_per_frame'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['stream_name_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['payload_extension_system_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['stream_name_count']; $i++) { + $streamName = array(); + + $streamName['language_id_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $streamName['stream_name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $streamName['stream_name'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $streamName['stream_name_length'])); + $offset += $streamName['stream_name_length']; + + $thisObject['stream_names'][$i] = $streamName; + } + + for ($i = 0; $i < $thisObject['payload_extension_system_count']; $i++) { + $payloadExtensionSystem = array(); + + $payloadExtensionSystem['extension_system_id'] = substr($asf_header_extension_object_data, $offset, 16); + $offset += 16; + $payloadExtensionSystem['extension_system_id_text'] = $this->BytestringToGUID($payloadExtensionSystem['extension_system_id']); + + $payloadExtensionSystem['extension_system_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + if ($payloadExtensionSystem['extension_system_size'] <= 0) { + break 2; + } + + $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $payloadExtensionSystem['extension_system_info_length'])); + $offset += $payloadExtensionSystem['extension_system_info_length']; + + $thisObject['payload_extension_systems'][$i] = $payloadExtensionSystem; + } + + break; + + case GETID3_ASF_Padding_Object: + // padding, skip it + break; + + case GETID3_ASF_Metadata_Object: + $thisObject['description_record_counts'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['description_record_counts']; $i++) { + $descriptionRecord = array(); + + $descriptionRecord['reserved_1'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); // must be zero + $offset += 2; - // Remove terminator 00 00 and convert UNICODE to Latin-1 + $descriptionRecord['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); + + $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $descriptionRecord['name'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['name_length']); + $offset += $descriptionRecord['name_length']; + + $descriptionRecord['data'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['data_length']); + $offset += $descriptionRecord['data_length']; + switch ($descriptionRecord['data_type']) { + case 0x0000: // Unicode string + break; + + case 0x0001: // BYTE array + // do nothing + break; + + case 0x0002: // BOOL + $descriptionRecord['data'] = (bool) getid3_lib::LittleEndian2Int($descriptionRecord['data']); + break; + + case 0x0003: // DWORD + case 0x0004: // QWORD + case 0x0005: // WORD + $descriptionRecord['data'] = getid3_lib::LittleEndian2Int($descriptionRecord['data']); + break; + + case 0x0006: // GUID + $descriptionRecord['data_text'] = $this->BytestringToGUID($descriptionRecord['data']); + break; + } + + $thisObject['description_record'][$i] = $descriptionRecord; + } + break; + + case GETID3_ASF_Language_List_Object: + $thisObject['language_id_record_counts'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['language_id_record_counts']; $i++) { + $languageIDrecord = array(); + + $languageIDrecord['language_id_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 1)); + $offset += 1; + + $languageIDrecord['language_id'] = substr($asf_header_extension_object_data, $offset, $languageIDrecord['language_id_length']); + $offset += $languageIDrecord['language_id_length']; + + $thisObject['language_id_record'][$i] = $languageIDrecord; + } + break; + + case GETID3_ASF_Metadata_Library_Object: + $thisObject['description_records_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['description_records_count']; $i++) { + $descriptionRecord = array(); + + $descriptionRecord['language_list_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); + + $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $descriptionRecord['name'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['name_length']); + $offset += $descriptionRecord['name_length']; + + $descriptionRecord['data'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['data_length']); + $offset += $descriptionRecord['data_length']; + + if (preg_match('#^WM/Picture$#', str_replace("\x00", '', trim($descriptionRecord['name'])))) { + $WMpicture = $this->ASF_WMpicture($descriptionRecord['data']); + foreach ($WMpicture as $key => $value) { + $descriptionRecord['data'] = $WMpicture; + } + unset($WMpicture); + } + + $thisObject['description_record'][$i] = $descriptionRecord; + } + break; + + default: + $unhandled_sections++; + if ($this->GUIDname($thisObject['guid_text'])) { + $this->getid3->info['warning'][] = 'unhandled Header Extension Object GUID "'.$this->GUIDname($thisObject['guid_text']).'" {'.$thisObject['guid_text'].'} at offset '.($offset - 16 - 8); + } else { + $this->getid3->info['warning'][] = 'unknown Header Extension Object GUID {'.$thisObject['guid_text'].'} in at offset '.($offset - 16 - 8); + } + break; + } + $HeaderExtensionObjectParsed[] = $thisObject; + + $objectOffset += $thisObject['size']; + } + return $HeaderExtensionObjectParsed; + } + + + static function ASFmetadataLibraryObjectDataTypeLookup($id) { + static $ASFmetadataLibraryObjectDataTypeLookup = array( + 0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters + 0x0001 => 'BYTE array', // The type of the data is implementation-specific + 0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values + 0x0003 => 'DWORD', // The data is 4 bytes long and should be interpreted as a 32-bit unsigned integer + 0x0004 => 'QWORD', // The data is 8 bytes long and should be interpreted as a 64-bit unsigned integer + 0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer + 0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID + ); + return (isset($ASFmetadataLibraryObjectDataTypeLookup[$id]) ? $ASFmetadataLibraryObjectDataTypeLookup[$id] : 'invalid'); + } + + function ASF_WMpicture(&$data) { + //typedef struct _WMPicture{ + // LPWSTR pwszMIMEType; + // BYTE bPictureType; + // LPWSTR pwszDescription; + // DWORD dwDataLen; + // BYTE* pbData; + //} WM_PICTURE; + + $WMpicture = array(); + + $offset = 0; + $WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1)); + $offset += 1; + $WMpicture['image_type'] = $this->WMpictureTypeLookup($WMpicture['image_type_id']); + $WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4)); + $offset += 4; + + $WMpicture['image_mime'] = ''; + do { + $next_byte_pair = substr($data, $offset, 2); + $offset += 2; + $WMpicture['image_mime'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $WMpicture['image_description'] = ''; + do { + $next_byte_pair = substr($data, $offset, 2); + $offset += 2; + $WMpicture['image_description'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $WMpicture['dataoffset'] = $offset; + $WMpicture['data'] = substr($data, $offset); + + $imageinfo = array(); + $WMpicture['image_mime'] = ''; + $imagechunkcheck = getid3_lib::GetDataImageSize($WMpicture['data'], $imageinfo); + unset($imageinfo); + if (!empty($imagechunkcheck)) { + $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); + } + if (!isset($this->getid3->info['asf']['comments']['picture'])) { + $this->getid3->info['asf']['comments']['picture'] = array(); + } + $this->getid3->info['asf']['comments']['picture'][] = array('data'=>$WMpicture['data'], 'image_mime'=>$WMpicture['image_mime']); + + return $WMpicture; + } + + + // Remove terminator 00 00 and convert UTF-16LE to Latin-1 static function TrimConvert($string) { return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', getid3_asf::TrimTerm($string)), ' '); } @@ -1683,7 +2016,6 @@ class getid3_asf return $string; } - } ?>
\ No newline at end of file diff --git a/modules/getid3/module.audio-video.bink.php b/modules/getid3/module.audio-video.bink.php index 2ebc6fe7..0a321396 100644 --- a/modules/getid3/module.audio-video.bink.php +++ b/modules/getid3/module.audio-video.bink.php @@ -14,26 +14,27 @@ ///////////////////////////////////////////////////////////////// -class getid3_bink +class getid3_bink extends getid3_handler { - function getid3_bink(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; -$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()'; +$info['error'][] = 'Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $fileTypeID = fread($fd, 3); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $fileTypeID = fread($this->getid3->fp, 3); switch ($fileTypeID) { case 'BIK': - return $this->ParseBink($fd, $ThisFileInfo); + return $this->ParseBink(); break; case 'SMK': - return $this->ParseSmacker($fd, $ThisFileInfo); + return $this->ParseSmacker(); break; default: - $ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"'; + $info['error'][] = 'Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"'; return false; break; } @@ -42,27 +43,29 @@ $ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this } - function ParseBink(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'bink'; - $ThisFileInfo['video']['dataformat'] = 'bink'; + function ParseBink() { + $info = &$this->getid3->info; + $info['fileformat'] = 'bink'; + $info['video']['dataformat'] = 'bink'; - $fileData = 'BIK'.fread($fd, 13); + $fileData = 'BIK'.fread($this->getid3->fp, 13); - $ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); - $ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); + $info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); + $info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); - if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) { - $ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + if (($info['avdataend'] - $info['avdataoffset']) != ($info['bink']['data_size'] + 8)) { + $info['error'][] = 'Probably truncated file: expecting '.$info['bink']['data_size'].' bytes, found '.($info['avdataend'] - $info['avdataoffset']); } return true; } - function ParseSmacker(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'smacker'; - $ThisFileInfo['video']['dataformat'] = 'smacker'; + function ParseSmacker() { + $info = &$this->getid3->info; + $info['fileformat'] = 'smacker'; + $info['video']['dataformat'] = 'smacker'; - return false; + return true; } } diff --git a/modules/getid3/module.audio-video.flv.php b/modules/getid3/module.audio-video.flv.php index e5f26696..20e9d4a2 100644 --- a/modules/getid3/module.audio-video.flv.php +++ b/modules/getid3/module.audio-video.flv.php @@ -8,31 +8,35 @@ // // // * version 0.1 (26 June 2005) // // // -// minor modifications by James Heinrich <info@getid3.org> // +// // // * version 0.1.1 (15 July 2005) // +// minor modifications by James Heinrich <info@getid3.org> // // // +// * version 0.2 (22 February 2006) // // Support for On2 VP6 codec and meta information // // by Steve Webster <steve.webster@featurecreep.com> // -// * version 0.2 (22 February 2006) // // // +// * version 0.3 (15 June 2006) // // Modified to not read entire file into memory // // by James Heinrich <info@getid3.org> // -// * version 0.3 (15 June 2006) // // // +// * version 0.4 (07 December 2007) // // Bugfixes for incorrectly parsed FLV dimensions // // and incorrect parsing of onMetaTag // // by Evgeny Moysevich <moysevich@gmail.com> // -// * version 0.4 (07 December 2007) // // // +// * version 0.5 (21 May 2009) // // Fixed parsing of audio tags and added additional codec // // details. The duration is now read from onMetaTag (if // // exists), rather than parsing whole file // // by Nigel Barnes <ngbarnes@hotmail.com> // -// * version 0.5 (21 May 2009) // // // +// * version 0.6 (24 May 2009) // // Better parsing of files with h264 video // // by Evgeny Moysevich <moysevichŘgmail*com> // -// * version 0.6 (24 May 2009) // +// // +// * version 0.6.1 (30 May 2011) // +// prevent infinite loops in expGolombUe() // // // ///////////////////////////////////////////////////////////////// // // @@ -63,77 +67,74 @@ define('H264_PROFILE_HIGH422', 122); define('H264_PROFILE_HIGH444', 144); define('H264_PROFILE_HIGH444_PREDICTIVE', 244); -class getid3_flv +class getid3_flv extends getid3_handler { + function Analyze() { + $info = &$this->getid3->info; - function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { -//$start_time = microtime(true); - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); - $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; - $FLVheader = fread($fd, 5); + $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; + $FLVheader = fread($this->getid3->fp, 5); - $ThisFileInfo['fileformat'] = 'flv'; - $ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3); - $ThisFileInfo['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); + $info['fileformat'] = 'flv'; + $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); + $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); - if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') { - $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"'; - unset($ThisFileInfo['flv']); - unset($ThisFileInfo['fileformat']); + $magic = 'FLV'; + if ($info['flv']['header']['signature'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; + unset($info['flv']); + unset($info['fileformat']); return false; } - $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); - $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); + $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); + $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); - $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4)); + $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4)); $FLVheaderFrameLength = 9; if ($FrameSizeDataLength > $FLVheaderFrameLength) { - fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); + fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); } -//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; - $Duration = 0; $found_video = false; $found_audio = false; $found_meta = false; $tagParsed = 0; - while (((ftell($fd) + 16) < $ThisFileInfo['avdataend']) && ($tagParsed <= 20 || !$found_meta)) { - $ThisTagHeader = fread($fd, 16); + while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && ($tagParsed <= 20 || !$found_meta)) { + $ThisTagHeader = fread($this->getid3->fp, 16); $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); - $NextOffset = ftell($fd) - 1 + $DataLength; + $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength; if ($Timestamp > $Duration) { $Duration = $Timestamp; } -//echo __LINE__.'['.ftell($fd).']=('.$TagType.')='.number_format(microtime(true) - $start_time, 3).'<br>'; - switch ($TagType) { case GETID3_FLV_TAG_AUDIO: if (!$found_audio) { $found_audio = true; - $ThisFileInfo['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F; - $ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03; - $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01; - $ThisFileInfo['flv']['audio']['audioType'] = $LastHeaderByte & 0x01; + $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F; + $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03; + $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01; + $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01; } break; case GETID3_FLV_TAG_VIDEO: if (!$found_video) { $found_video = true; - $ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; + $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; - $FLVvideoHeader = fread($fd, 11); + $FLVvideoHeader = fread($this->getid3->fp, 11); - if ($ThisFileInfo['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { + if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { // this code block contributed by: moysevichŘgmail*com $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); @@ -151,37 +152,37 @@ class getid3_flv //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); // read the first SequenceParameterSet - $sps = fread($fd, $spsSize); + $sps = fread($this->getid3->fp, $spsSize); if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red $spsReader = new AVCSequenceParameterSetReader($sps); $spsReader->readData(); - $ThisFileInfo['video']['resolution_x'] = $spsReader->getWidth(); - $ThisFileInfo['video']['resolution_y'] = $spsReader->getHeight(); + $info['video']['resolution_x'] = $spsReader->getWidth(); + $info['video']['resolution_y'] = $spsReader->getHeight(); } } } // end: moysevichŘgmail*com - } elseif ($ThisFileInfo['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) { + } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) { $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7; $PictureSizeType = $PictureSizeType & 0x0007; - $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType; + $info['flv']['header']['videoSizeType'] = $PictureSizeType; switch ($PictureSizeType) { case 0: //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); //$PictureSizeEnc <<= 1; - //$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; + //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); //$PictureSizeEnc <<= 1; - //$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; + //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); $PictureSizeEnc['x'] >>= 7; $PictureSizeEnc['y'] >>= 7; - $ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; - $ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; + $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; + $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; break; case 1: @@ -189,43 +190,43 @@ class getid3_flv $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)); $PictureSizeEnc['x'] >>= 7; $PictureSizeEnc['y'] >>= 7; - $ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; - $ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; + $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; + $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; break; case 2: - $ThisFileInfo['video']['resolution_x'] = 352; - $ThisFileInfo['video']['resolution_y'] = 288; + $info['video']['resolution_x'] = 352; + $info['video']['resolution_y'] = 288; break; case 3: - $ThisFileInfo['video']['resolution_x'] = 176; - $ThisFileInfo['video']['resolution_y'] = 144; + $info['video']['resolution_x'] = 176; + $info['video']['resolution_y'] = 144; break; case 4: - $ThisFileInfo['video']['resolution_x'] = 128; - $ThisFileInfo['video']['resolution_y'] = 96; + $info['video']['resolution_x'] = 128; + $info['video']['resolution_y'] = 96; break; case 5: - $ThisFileInfo['video']['resolution_x'] = 320; - $ThisFileInfo['video']['resolution_y'] = 240; + $info['video']['resolution_x'] = 320; + $info['video']['resolution_y'] = 240; break; case 6: - $ThisFileInfo['video']['resolution_x'] = 160; - $ThisFileInfo['video']['resolution_y'] = 120; + $info['video']['resolution_x'] = 160; + $info['video']['resolution_y'] = 120; break; default: - $ThisFileInfo['video']['resolution_x'] = 0; - $ThisFileInfo['video']['resolution_y'] = 0; + $info['video']['resolution_x'] = 0; + $info['video']['resolution_y'] = 0; break; } } - $ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['video']['resolution_x'] / $ThisFileInfo['video']['resolution_y']; + $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; } break; @@ -233,29 +234,29 @@ class getid3_flv case GETID3_FLV_TAG_META: if (!$found_meta) { $found_meta = true; - fseek($fd, -1, SEEK_CUR); - $datachunk = fread($fd, $DataLength); + fseek($this->getid3->fp, -1, SEEK_CUR); + $datachunk = fread($this->getid3->fp, $DataLength); $AMFstream = new AMFStream($datachunk); $reader = new AMFReader($AMFstream); $eventName = $reader->readData(); - $ThisFileInfo['flv']['meta'][$eventName] = $reader->readData(); + $info['flv']['meta'][$eventName] = $reader->readData(); unset($reader); $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate'); foreach ($copykeys as $sourcekey => $destkey) { - if (isset($ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey])) { + if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) { switch ($sourcekey) { case 'width': case 'height': - $ThisFileInfo['video'][$destkey] = intval(round($ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey])); + $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey])); break; case 'audiodatarate': - $ThisFileInfo['audio'][$destkey] = $ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey]; + $info['audio'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey]; break; case 'videodatarate': case 'frame_rate': default: - $ThisFileInfo['video'][$destkey] = $ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey]; + $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey]; break; } } @@ -268,42 +269,42 @@ class getid3_flv break; } - fseek($fd, $NextOffset, SEEK_SET); + fseek($this->getid3->fp, $NextOffset, SEEK_SET); // Increase parsed tag count: break out of loop if more than 20 tags parsed $tagParsed++; } - $ThisFileInfo['playtime_seconds'] = $Duration / 1000; - if ($ThisFileInfo['playtime_seconds'] > 0) { - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $Duration / 1000; + if ($info['playtime_seconds'] > 0) { + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } - if ($ThisFileInfo['flv']['header']['hasAudio']) { - $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']); - $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']); - $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']); + if ($info['flv']['header']['hasAudio']) { + $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']); + $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']); + $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']); - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo - $ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed - $ThisFileInfo['audio']['dataformat'] = 'flv'; + $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo + $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed + $info['audio']['dataformat'] = 'flv'; } - if (!empty($ThisFileInfo['flv']['header']['hasVideo'])) { - $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']); - $ThisFileInfo['video']['dataformat'] = 'flv'; - $ThisFileInfo['video']['lossless'] = false; + if (!empty($info['flv']['header']['hasVideo'])) { + $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']); + $info['video']['dataformat'] = 'flv'; + $info['video']['lossless'] = false; } // Set information from meta - if (isset($ThisFileInfo['flv']['meta']['onMetaData']['duration'])) { - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flv']['meta']['onMetaData']['duration']; - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + if (isset($info['flv']['meta']['onMetaData']['duration'])) { + $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } - if (isset($ThisFileInfo['flv']['meta']['onMetaData']['audiocodecid'])) { - $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['meta']['onMetaData']['audiocodecid']); + if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { + $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']); } - if (isset($ThisFileInfo['flv']['meta']['onMetaData']['videocodecid'])) { - $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['meta']['onMetaData']['videocodecid']); + if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { + $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']); } return true; } @@ -693,6 +694,11 @@ class AVCSequenceParameterSetReader { while ($bit == 0) { $significantBits++; $bit = $this->getBit(); + + if ($significantBits > 31) { + // something is broken, this is an emergency escape to prevent infinite loops + return 0; + } } return (1 << $significantBits) + $this->getBits($significantBits) - 1; } diff --git a/modules/getid3/module.audio-video.matroska.php b/modules/getid3/module.audio-video.matroska.php index 47f7afa6..59701a32 100644 --- a/modules/getid3/module.audio-video.matroska.php +++ b/modules/getid3/module.audio-video.matroska.php @@ -14,9 +14,10 @@ ///////////////////////////////////////////////////////////////// +// from: http://www.matroska.org/technical/specs/index.html define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation. define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements. -define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here. +define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>. define('EBML_ID_INFO', 0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file. define('EBML_ID_TRACKS', 0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described. define('EBML_ID_SEGMENT', 0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment. @@ -205,43 +206,51 @@ define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] -- define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block. -class getid3_matroska +class getid3_matroska extends getid3_handler { - var $read_buffer_size = 32768; // size of read buffer, 32kB is default - var $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 - var $warnings = array(); + var $read_buffer_size = 32768; // size of read buffer, 32kB is default + var $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 + var $warnings = array(); + var $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 - function getid3_matroska(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // http://www.matroska.org/technical/specs/index.html#EBMLBasics - $offset = $ThisFileInfo['avdataoffset']; + $offset = $info['avdataoffset']; $EBMLdata = ''; $EBMLdata_offset = $offset; - if (!getid3_lib::intValueSupported($ThisFileInfo['avdataend'])) { - $this->warnings[] = 'This version of getID3() may or may not correctly handle Matroska files larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + if (!getid3_lib::intValueSupported($info['avdataend'])) { + $this->getid3->warning('This version of getID3() ['.$this->getid3->version().'] may or may not correctly handle Matroska files larger than '.round(PHP_INT_MAX / 1073741824).'GB'); } - while ($offset < $ThisFileInfo['avdataend']) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + while ($offset < $info['avdataend']) { + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $top_element_offset = $offset; $top_element_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $top_element_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); if ($top_element_length === false) { - $this->warnings[] = 'invalid chunk length at '.$top_element_offset; - $offset = pow(2, 63); + $this->getid3->warning('invalid chunk length at '.$top_element_offset); + $offset = PHP_INT_MAX + 1; break; } $top_element_endoffset = $offset + $top_element_length; switch ($top_element_id) { case EBML_ID_EBML: - $ThisFileInfo['fileformat'] = 'matroska'; - $ThisFileInfo['matroska']['header']['offset'] = $top_element_offset; - $ThisFileInfo['matroska']['header']['length'] = $top_element_length; + $info['fileformat'] = 'matroska'; + $info['matroska']['header']['offset'] = $top_element_offset; + $info['matroska']['header']['length'] = $top_element_length; while ($offset < $top_element_endoffset) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $element_data = array(); $element_data_offset = $offset; $element_data['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); @@ -263,23 +272,32 @@ class getid3_matroska case EBML_ID_DOCTYPE: $element_data['data'] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']), "\x00"); break; + case EBML_ID_CRC32: + // probably not useful, ignore + unset($element_data); + break; default: - $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data_offset; + $this->getid3->warning('Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data_offset); break; } $offset = $end_offset; - $ThisFileInfo['matroska']['header']['elements'][] = $element_data; + if (!empty($element_data)) { + $info['matroska']['header']['elements'][] = $element_data; + } } break; case EBML_ID_SEGMENT: - $ThisFileInfo['matroska']['segment'][0]['offset'] = $top_element_offset; - $ThisFileInfo['matroska']['segment'][0]['length'] = $top_element_length; + $info['matroska']['segment'][0]['offset'] = $top_element_offset; + $info['matroska']['segment'][0]['length'] = $top_element_length; $segment_key = -1; - while ($offset < $ThisFileInfo['avdataend']) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + while ($offset < $info['avdataend']) { + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $element_data = array(); $element_data['offset'] = $offset; @@ -287,9 +305,9 @@ class getid3_matroska $element_data['id_name'] = $this->EBMLidName($element_data['id']); $element_data['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); if ($element_data['length'] === false) { - $this->warnings[] = 'invalid chunk length at '.$element_data['offset']; - //$offset = pow(2, 63); - $offset = $ThisFileInfo['avdataend']; + $this->getid3->warning('invalid chunk length at '.$element_data['offset']); + //$offset = PHP_INT_MAX + 1; + $offset = $info['avdataend']; break; } $element_end = $offset + $element_data['length']; @@ -298,11 +316,11 @@ class getid3_matroska // // too many cluster entries, probably not useful // break; case false: - $this->warnings[] = 'invalid ID at '.$element_data['offset']; + $this->getid3->warning('invalid ID at '.$element_data['offset']); $offset = $element_end; continue 3; default: - $ThisFileInfo['matroska']['segments'][] = $element_data; + $info['matroska']['segments'][] = $element_data; break; } $segment_key++; @@ -310,7 +328,10 @@ class getid3_matroska switch ($element_data['id']) { case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $seek_entry = array(); $seek_entry['offset'] = $offset; $seek_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); @@ -320,7 +341,10 @@ class getid3_matroska switch ($seek_entry['id']) { case EBML_ID_SEEK: // Contains a single seek entry to an EBML element while ($offset < $seek_end_offset) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $value = substr($EBMLdata, $offset - $EBMLdata_offset, $length); @@ -334,23 +358,33 @@ class getid3_matroska case EBML_ID_SEEKPOSITION: $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($value); break; + case EBML_ID_CRC32: + // probably not useful, ignore + //$seek_entry['crc32'] = getid3_lib::PrintHexBytes($value, true, false, false); + unset($seek_entry); + break; default: - $ThisFileInfo['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$id.') at '.$offset; + $info['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$id.') at '.$offset; break; } } - $ThisFileInfo['matroska']['seek'][] = $seek_entry; + if (!empty($seek_entry)) { + $info['matroska']['seek'][] = $seek_entry; + } //switch ($seek_entry['target_id']) { // case EBML_ID_CLUSTER: // // too many cluster seek points, probably not useful // break; // default: - // $ThisFileInfo['matroska']['seek'][] = $seek_entry; + // $info['matroska']['seek'][] = $seek_entry; // break; //} break; + case EBML_ID_CRC32: + // probably not useful, ignore + break; default: - $this->warnings[] = 'Unhandled seekhead element ['.basename(__FILE__).':'.__LINE__.'] ('.$seek_entry['id'].') at '.$offset; + $this->getid3->warning('Unhandled seekhead element ['.basename(__FILE__).':'.__LINE__.'] ('.$seek_entry['id'].'::'.$seek_entry['id_name'].') at '.$offset); break; } $offset = $seek_end_offset; @@ -358,19 +392,25 @@ class getid3_matroska break; case EBML_ID_TRACKS: // information about all tracks in segment - $ThisFileInfo['matroska']['tracks'] = $element_data; + $info['matroska']['tracks'] = $element_data; while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $track_entry = array(); $track_entry['offset'] = $offset; $track_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $track_entry['id_name'] = $this->EBMLidName($track_entry['id']); $track_entry['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); - $track_entry_endoffset = $offset + $track_entry['length']; + $track_entry_endoffset = $offset + $track_entry['length']; // $track_entry['offset'] is not the same as $offset, even though they were set equal a few lines up: $offset has been automagically incremented by readEMLint() switch ($track_entry['id']) { case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements. while ($offset < $track_entry_endoffset) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -399,6 +439,18 @@ class getid3_matroska $track_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00"); break; + // thought maybe it was a nice wFormatTag entry, but it's not :( + //case EBML_ID_CODECPRIVATE: + //$track_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00"); + //if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { + // $track_entry[$subelement_idname.'_decoded'] = getid3_riff::RIFFparseWAVEFORMATex($track_entry[$subelement_idname]); + // if (isset($track_entry[$subelement_idname.'_decoded']['raw']['wFormatTag'])) { + // } + //} else { + // $this->getid3->warning('failed to include "module.audio-video.riff.php" for parsing codec private data'); + //} + //break; + case EBML_ID_FLAGENABLED: case EBML_ID_FLAGDEFAULT: case EBML_ID_FLAGFORCED: @@ -409,7 +461,10 @@ class getid3_matroska case EBML_ID_VIDEO: while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -439,7 +494,7 @@ class getid3_matroska $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00"); break; default: - $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -449,14 +504,17 @@ class getid3_matroska if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { $track_entry['codec_private_parsed'] = getid3_riff::ParseBITMAPINFOHEADER($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)]); } else { - $this->warnings[] = 'Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"'; + $this->getid3->warning('Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"'); } } break; case EBML_ID_AUDIO: while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -475,7 +533,7 @@ class getid3_matroska $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00"); break; default: - $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled track.audio element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -484,7 +542,10 @@ class getid3_matroska case EBML_ID_CONTENTENCODINGS: while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -493,7 +554,10 @@ class getid3_matroska switch ($sub_subelement_id) { case EBML_ID_CONTENTENCODING: while ($offset < $sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_subelement_offset = $offset; $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -507,7 +571,10 @@ class getid3_matroska break; case EBML_ID_CONTENTCOMPRESSION: while ($offset < $sub_sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_sub_subelement_offset = $offset; $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -521,7 +588,7 @@ class getid3_matroska $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length); break; default: - $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $sub_sub_sub_subelement_end; @@ -530,7 +597,10 @@ class getid3_matroska case EBML_ID_CONTENTENCRYPTION: while ($offset < $sub_sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_sub_subelement_offset = $offset; $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -548,7 +618,7 @@ class getid3_matroska $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length); break; default: - $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $sub_sub_sub_subelement_end; @@ -556,40 +626,53 @@ class getid3_matroska break; default: - $this->warnings[] = 'Unhandled track.contentencodings.contentencoding element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled track.contentencodings.contentencoding element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $sub_sub_subelement_end; } break; default: - $this->warnings[] = 'Unhandled track.contentencodings element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled track.contentencodings element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $sub_subelement_end; } break; + case EBML_ID_CRC32: + // probably not useful, ignore + break; + default: - $this->warnings[] = 'Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; } break; + + case EBML_ID_CRC32: + // probably not useful, ignore + $offset = $track_entry_endoffset; + break; + default: - $this->warnings[] = 'Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$track_entry['id'].'::'.$track_entry['id_name'].') at '.$track_entry['offset']; + $this->getid3->warning('Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$track_entry['id'].'::'.$track_entry['id_name'].') at '.$track_entry['offset']); $offset = $track_entry_endoffset; break; } - $ThisFileInfo['matroska']['tracks']['tracks'][] = $track_entry; + $info['matroska']['tracks']['tracks'][] = $track_entry; } break; case EBML_ID_INFO: // Contains the position of other level 1 elements $info_entry = array(); while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -622,20 +705,27 @@ class getid3_matroska case EBML_ID_MUXINGAPP: case EBML_ID_WRITINGAPP: $info_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00"); + $info['matroska']['comments'][strtolower($subelement_idname)][] = $info_entry[$subelement_idname]; + break; + case EBML_ID_CRC32: + // probably not useful, ignore break; default: - $this->warnings[] = 'Unhandled info element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled info element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; } - $ThisFileInfo['matroska']['info'][] = $info_entry; + $info['matroska']['info'][] = $info_entry; break; case EBML_ID_CUES: $cues_entry = array(); while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -645,7 +735,10 @@ class getid3_matroska case EBML_ID_CUEPOINT: $cuepoint_entry = array(); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -654,7 +747,10 @@ class getid3_matroska switch ($sub_subelement_id) { case EBML_ID_CUETRACKPOSITIONS: while ($offset < $sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_subelement_offset = $offset; $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -665,7 +761,7 @@ class getid3_matroska $cuepoint_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length)); break; default: - $this->warnings[] = 'Unhandled cues.cuepoint.cuetrackpositions element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset; + $this->getid3->warning('Unhandled cues.cuepoint.cuetrackpositions element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -675,7 +771,7 @@ class getid3_matroska $cuepoint_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length)); break; default: - $this->warnings[] = 'Unhandled cues.cuepoint element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled cues.cuepoint element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -683,32 +779,43 @@ class getid3_matroska $cues_entry[] = $cuepoint_entry; $offset = $sub_subelement_end; break; + + case EBML_ID_CRC32: + // probably not useful, ignore + break; + default: - $this->warnings[] = 'Unhandled cues element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled cues element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; } - $ThisFileInfo['matroska']['cues'] = $cues_entry; + $info['matroska']['cues'] = $cues_entry; break; case EBML_ID_TAGS: $tags_entry = array(); while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_end = $offset + $subelement_length; + $tag_entry = array(); switch ($subelement_id) { case EBML_ID_WRITINGAPP: - $tags_entry[$subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length); + $tag_entry[$subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length); break; case EBML_ID_TAG: - $tag_entry = array(); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -718,7 +825,10 @@ class getid3_matroska case EBML_ID_TARGETS: $targets_entry = array(); while ($offset < $sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_subelement_offset = $offset; $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -726,6 +836,9 @@ class getid3_matroska $sub_sub_subelement_end = $offset + $sub_sub_subelement_length; switch ($sub_sub_subelement_id) { case EBML_ID_TARGETTYPEVALUE: + $targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length)); + $targets_entry[strtolower($sub_sub_subelement_idname).'_long'] = $this->MatroskaTargetTypeValue($targets_entry[$sub_sub_subelement_idname]); + break; case EBML_ID_EDITIONUID: case EBML_ID_CHAPTERUID: case EBML_ID_ATTACHMENTUID: @@ -734,68 +847,57 @@ class getid3_matroska $targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length)); break; default: - $this->warnings[] = 'Unhandled tag.targets element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset; + $this->getid3->warning('Unhandled tag.targets element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset); break; } $offset = $sub_sub_subelement_end; } $tag_entry[$sub_subelement_idname][] = $targets_entry; break; + case EBML_ID_SIMPLETAG: - $simpletag_entry = array(); - while ($offset < $sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); - $sub_sub_subelement_offset = $offset; - $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); - $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); - $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); - $sub_sub_subelement_end = $offset + $sub_sub_subelement_length; - switch ($sub_sub_subelement_id) { - case EBML_ID_TAGNAME: - case EBML_ID_TAGLANGUAGE: - case EBML_ID_TAGSTRING: - case EBML_ID_TAGBINARY: - $simpletag_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length); - break; - case EBML_ID_TAGDEFAULT: - $simpletag_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length)); - break; - default: - $this->warnings[] = 'Unhandled tag.simpletag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset; - break; - } - $offset = $sub_sub_subelement_end; - } - $tag_entry[$sub_subelement_idname][] = $simpletag_entry; + //$tag_entry[$sub_subelement_idname][] = $simpletag_entry; + $tag_entry[$sub_subelement_idname][] = $this->Handle_EMBL_ID_SIMPLETAG($offset, $sub_subelement_end); break; + case EBML_ID_TARGETTYPE: $tag_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length); break; + case EBML_ID_TRACKUID: $tag_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length)); break; + default: - $this->warnings[] = 'Unhandled tags.tag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled tags.tag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; } - $tags_entry['tags'][] = $tag_entry; $offset = $sub_subelement_end; break; + + case EBML_ID_CRC32: + // probably not useful, ignore + break; + default: - $this->warnings[] = 'Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } + $tags_entry['tags'][] = $tag_entry; $offset = $subelement_end; } - $ThisFileInfo['matroska']['tags'] = $tags_entry; + $info['matroska']['tags'] = $tags_entry['tags']; break; case EBML_ID_ATTACHMENTS: while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -805,7 +907,10 @@ class getid3_matroska case EBML_ID_ATTACHEDFILE: $attachedfile_entry = array(); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -815,15 +920,50 @@ class getid3_matroska case EBML_ID_FILEDESCRIPTION: case EBML_ID_FILENAME: case EBML_ID_FILEMIMETYPE: - $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length); + $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length); break; case EBML_ID_FILEDATA: $attachedfile_entry['data_offset'] = $offset; $attachedfile_entry['data_length'] = $sub_subelement_length; - if ($sub_subelement_length < 1024) { - $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length); - } + do { + if ($this->inline_attachments === false) { + // skip entirely + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $sub_subelement_length) { + // too big, skip + $this->getid3->warning('attachment at '.$sub_subelement_offset.' is too large to process inline ('.number_format($sub_subelement_length).' bytes)'); + break; + } + } 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)) { + // cannot write, skip + $this->getid3->warning('attachment at '.$sub_subelement_offset.' cannot be saved to "'.$this->inline_attachments.'" (not writable)'); + break; + } + } + // if we get this far, must be OK + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset, $sub_subelement_length)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } + $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length); + if (is_string($this->inline_attachments)) { + $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$attachedfile_entry['data_offset']; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $attachedfile_entry[$sub_subelement_idname]); + } else { + $this->getid3->warning('attachment at '.$sub_subelement_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'); + } + $attachedfile_entry[$sub_subelement_idname.'_filename'] = $destination_filename; + unset($attachedfile_entry[$sub_subelement_idname]); + } + } while (false); break; case EBML_ID_FILEUID: @@ -831,16 +971,33 @@ class getid3_matroska break; default: - $this->warnings[] = 'Unhandled attachment.attachedfile element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled attachment.attachedfile element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; } - $ThisFileInfo['matroska']['attachments'][] = $attachedfile_entry; + if (!empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)]) && !empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)]) && preg_match('#^image/#i', $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)])) { + if (($this->inline_attachments === true) || (is_int($this->inline_attachments) && ($this->inline_attachments >= strlen($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)])))) { + $attachedfile_entry['data'] = $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)]; + $attachedfile_entry['image_mime'] = $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)]; + $info['matroska']['comments']['picture'][] = array('data'=>$attachedfile_entry['data'], 'image_mime'=>$attachedfile_entry['image_mime'], 'filename'=>(!empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILENAME)]) ? $attachedfile_entry[$this->EBMLidName(EBML_ID_FILENAME)] : '')); + unset($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)], $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)]); + } + } + if (!empty($attachedfile_entry['image_mime']) && preg_match('#^image/#i', $attachedfile_entry['image_mime'])) { + // don't add a second copy of attached images, which are grouped under the standard location [comments][picture] + } else { + $info['matroska']['attachments'][] = $attachedfile_entry; + } $offset = $sub_subelement_end; break; + + case EBML_ID_CRC32: + // probably not useful, ignore + break; + default: - $this->warnings[] = 'Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; @@ -850,7 +1007,10 @@ class getid3_matroska case EBML_ID_CHAPTERS: // not important to us, contains mostly actual audio/video data, ignore while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -860,7 +1020,10 @@ class getid3_matroska case EBML_ID_EDITIONENTRY: $editionentry_entry = array(); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -878,7 +1041,10 @@ class getid3_matroska case EBML_ID_CHAPTERATOM: $chapteratom_entry = array(); while ($offset < $sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_subelement_offset = $offset; $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -901,7 +1067,10 @@ class getid3_matroska case EBML_ID_CHAPTERTRACK: $chaptertrack_entry = array(); while ($offset < $sub_sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_sub_subelement_offset = $offset; $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); @@ -912,7 +1081,7 @@ class getid3_matroska $chaptertrack_entry[$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length)); break; default: - $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chaptertrack element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset; + $this->getid3->warning('Unhandled chapters.editionentry.chapteratom.chaptertrack element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset); break; } $offset = $sub_sub_sub_subelement_end; @@ -922,7 +1091,10 @@ class getid3_matroska case EBML_ID_CHAPTERDISPLAY: $chapterdisplay_entry = array(); while ($offset < $sub_sub_subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_sub_sub_subelement_offset = $offset; $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_sub_subelement_id); @@ -935,7 +1107,7 @@ class getid3_matroska $chapterdisplay_entry[$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length); break; default: - $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chapterdisplay element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset; + $this->getid3->warning('Unhandled chapters.editionentry.chapteratom.chapterdisplay element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset); break; } $offset = $sub_sub_sub_subelement_end; @@ -943,7 +1115,7 @@ class getid3_matroska $chapteratom_entry[$sub_sub_subelement_idname][] = $chapterdisplay_entry; break; default: - $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset; + $this->getid3->warning('Unhandled chapters.editionentry.chapteratom element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset); break; } $offset = $sub_sub_subelement_end; @@ -951,16 +1123,16 @@ class getid3_matroska $editionentry_entry[$sub_subelement_idname][] = $chapteratom_entry; break; default: - $this->warnings[] = 'Unhandled chapters.editionentry element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled chapters.editionentry element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; } - $ThisFileInfo['matroska']['chapters'][] = $editionentry_entry; + $info['matroska']['chapters'][] = $editionentry_entry; $offset = $sub_subelement_end; break; default: - $this->warnings[] = 'Unhandled chapters element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled chapters element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; @@ -971,13 +1143,16 @@ class getid3_matroska case EBML_ID_VOID: // padding, ignore $void_entry = array(); $void_entry['offset'] = $offset; - $ThisFileInfo['matroska']['void'][] = $void_entry; + $info['matroska']['void'][] = $void_entry; break; case EBML_ID_CLUSTER: // not important to us, contains mostly actual audio/video data, ignore $cluster_entry = array(); while ($offset < $element_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $subelement_offset = $offset; $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $subelement_idname = $this->EBMLidName($subelement_id); @@ -993,7 +1168,10 @@ class getid3_matroska case EBML_ID_CLUSTERSILENTTRACKS: $cluster_silent_tracks = array(); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -1004,7 +1182,7 @@ class getid3_matroska $cluster_silent_tracks[] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length)); break; default: - $this->warnings[] = 'Unhandled clusters.silenttracks element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled clusters.silenttracks element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -1016,7 +1194,10 @@ class getid3_matroska case EBML_ID_CLUSTERBLOCKGROUP: $cluster_block_group = array('offset'=>$offset); while ($offset < $subelement_end) { - $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset); + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } $sub_subelement_offset = $offset; $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); $sub_subelement_idname = $this->EBMLidName($sub_subelement_id); @@ -1044,8 +1225,9 @@ class getid3_matroska $offset += 1; } } - if (!isset($ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) { - $ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']] = $offset; + if (!isset($info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) { + $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['offset'] = $offset; + $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['length'] = $subelement_length; } $cluster_block_group[$sub_subelement_idname] = $cluster_block_data; break; @@ -1060,7 +1242,7 @@ class getid3_matroska break; default: - $this->warnings[] = 'Unhandled clusters.blockgroup element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset; + $this->getid3->warning('Unhandled clusters.blockgroup element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset); break; } $offset = $sub_subelement_end; @@ -1093,23 +1275,24 @@ class getid3_matroska } } - if (!isset($ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) { - $ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']] = $offset; + if (!isset($info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) { + $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['offset'] = $offset; + $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['length'] = $subelement_length; } $cluster_block_group[$sub_subelement_idname] = $cluster_block_data; break; default: - $this->warnings[] = 'Unhandled cluster element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset; + $this->getid3->warning('Unhandled cluster element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset); break; } $offset = $subelement_end; } - $ThisFileInfo['matroska']['cluster'][] = $cluster_entry; + $info['matroska']['cluster'][] = $cluster_entry; // check to see if all the data we need exists already, if so, break out of the loop - if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) { - if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) { + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { break 2; } } @@ -1117,9 +1300,9 @@ class getid3_matroska default: if ($element_data['id_name'] == dechex($element_data['id'])) { - $ThisFileInfo['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].') at '.$element_data_offset; + $info['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].') at '.$element_data_offset; } else { - $this->warnings[] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data['offset']; + $this->getid3->warning('Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data['offset']); } break; } @@ -1129,7 +1312,7 @@ class getid3_matroska default: - $ThisFileInfo['error'][] = 'Unhandled chunk ['.basename(__FILE__).':'.__LINE__.'] ('.$top_element_id.') at '.$offset; + $info['error'][] = 'Unhandled chunk ['.basename(__FILE__).':'.__LINE__.'] ('.$top_element_id.') at '.$offset; break; } $offset = $top_element_endoffset; @@ -1137,18 +1320,28 @@ class getid3_matroska - if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) { - foreach ($ThisFileInfo['matroska']['info'] as $key => $infoarray) { + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + foreach ($info['matroska']['info'] as $key => $infoarray) { if (isset($infoarray['Duration'])) { // TimecodeScale is how many nanoseconds each Duration unit is - $ThisFileInfo['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000); + $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000); break; } } } - if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) { - foreach ($ThisFileInfo['matroska']['tracks']['tracks'] as $key => $trackarray) { + + if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) { + foreach ($info['matroska']['tags'] as $key => $infoarray) { + $this->ExtractCommentsSimpleTag($infoarray); + } + } + + if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { + foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) { $track_info = array(); + if (isset($trackarray['FlagDefault'])) { + $track_info['default'] = $trackarray['FlagDefault']; + } switch (isset($trackarray['TrackType']) ? $trackarray['TrackType'] : '') { case 1: // Video if (!empty($trackarray['PixelWidth'])) { $track_info['resolution_x'] = $trackarray['PixelWidth']; } @@ -1158,10 +1351,10 @@ class getid3_matroska if (!empty($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); } if (!empty($trackarray['CodecID'])) { $track_info['dataformat'] = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']); } if (!empty($trackarray['codec_private_parsed']['fourcc'])) { $track_info['fourcc'] = $trackarray['codec_private_parsed']['fourcc']; } - $ThisFileInfo['video']['streams'][] = $track_info; - if (isset($track_info['resolution_x']) && empty($ThisFileInfo['video']['resolution_x'])) { + $info['video']['streams'][] = $track_info; + if (isset($track_info['resolution_x']) && empty($info['video']['resolution_x'])) { foreach ($track_info as $key => $value) { - $ThisFileInfo['video'][$key] = $value; + $info['video'][$key] = $value; } } break; @@ -1179,120 +1372,141 @@ class getid3_matroska case 'A_AC3': if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - if (isset($ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) { - $ac3_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]); - $getid3_ac3 = new getid3_ac3($fd, $ac3_thisfileinfo); - $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $ac3_thisfileinfo; - if (!empty($ac3_thisfileinfo['error'])) { - foreach ($ac3_thisfileinfo['error'] as $newerror) { - $this->warnings[] = 'getid3_ac3() says: ['.$newerror.']'; + if (isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'])) { + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; + $getid3_ac3 = new getid3_ac3($getid3_temp); + $getid3_ac3->Analyze(); + unset($getid3_temp->info['ac3']['GETID3_VERSION']); + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ac3']; + if (!empty($getid3_temp->info['error'])) { + foreach ($getid3_temp->info['error'] as $newerror) { + $this->getid3->warning('getid3_ac3() says: ['.$newerror.']'); } } - if (!empty($ac3_thisfileinfo['warning'])) { - foreach ($ac3_thisfileinfo['warning'] as $newerror) { - $this->warnings[] = 'getid3_ac3() says: ['.$newerror.']'; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->getid3->warning('getid3_ac3() says: ['.$newerror.']'); } } - if (isset($ac3_thisfileinfo['audio']) && is_array($ac3_thisfileinfo['audio'])) { - foreach ($ac3_thisfileinfo['audio'] as $key => $value) { + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { $track_info[$key] = $value; } } - unset($ac3_thisfileinfo); - unset($getid3_ac3); + unset($getid3_temp, $getid3_ac3); } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $ThisFileInfo[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set'; + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'][offset] not set'); } } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ac3.php"'; + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ac3.php"'); } break; case 'A_DTS': - $dts_offset = $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]; - // this is a NASTY hack, but sometimes audio data is off by a byte or two and not sure why, email info@getid3.org if you can explain better - fseek($fd, $dts_offset, SEEK_SET); - $magic_test = fread($fd, 8); - for ($i = 0; $i < 4; $i++) { - // look to see if DTS "magic" is here, if so adjust offset by that many bytes - if (substr($magic_test, $i, 4) == "\x7F\xFE\x80\x01") { - $dts_offset += $i; - break; - } - } - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, false)) { - $dts_thisfileinfo = array('avdataoffset'=>$dts_offset); - $getid3_dts = new getid3_dts($fd, $dts_thisfileinfo); - $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $dts_thisfileinfo; - if (!empty($dts_thisfileinfo['error'])) { - foreach ($dts_thisfileinfo['error'] as $newerror) { - $this->warnings[] = 'getid3_dts() says: ['.$newerror.']'; + if (isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'])) { + $dts_offset = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; + // this is a NASTY hack, but sometimes audio data is off by a byte or two and not sure why, email info@getid3.org if you can explain better + fseek($this->getid3->fp, $dts_offset, SEEK_SET); + $magic_test = fread($this->getid3->fp, 8); + for ($i = 0; $i < 4; $i++) { + // look to see if DTS "magic" is here, if so adjust offset by that many bytes + if (substr($magic_test, $i, 4) == "\x7F\xFE\x80\x01") { + $dts_offset += $i; + break; } } - if (!empty($dts_thisfileinfo['warning'])) { - foreach ($dts_thisfileinfo['warning'] as $newerror) { - $this->warnings[] = 'getid3_dts() says: ['.$newerror.']'; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, false)) { + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $dts_offset; + $getid3_dts = new getid3_dts($getid3_temp); + $getid3_dts->Analyze(); + unset($getid3_temp->info['dts']['GETID3_VERSION']); + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['dts']; + if (!empty($getid3_temp->info['error'])) { + foreach ($getid3_temp->info['error'] as $newerror) { + $this->getid3->warning('getid3_dts() says: ['.$newerror.']'); + } } - } - if (isset($dts_thisfileinfo['audio']) && is_array($dts_thisfileinfo['audio'])) { - foreach ($dts_thisfileinfo['audio'] as $key => $value) { - $track_info[$key] = $value; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->getid3->warning('getid3_dts() says: ['.$newerror.']'); + } } + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { + $track_info[$key] = $value; + } + } + unset($getid3_temp, $getid3_dts); + } else { + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.dts.php"'); } - unset($dts_thisfileinfo); - unset($getid3_dts); } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.dts.php"'; + $this->getid3->warning('Unable to parse audio data for track "'.$trackarray['TrackNumber'].'" in ['.basename(__FILE__).':'.__LINE__.'] data offset is unknown'); } break; case 'A_AAC': -$this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems parsing AAC audio in Matroska containers ['.basename(__FILE__).':'.__LINE__.']'; +$this->getid3->warning('This version of getID3() [v'.$this->getid3->version().'] has problems parsing AAC audio in Matroska containers ['.basename(__FILE__).':'.__LINE__.']'); if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.aac.php', __FILE__, false)) { - // $aac_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]); - // $getid3_aac = new getid3_aac($fd, $aac_thisfileinfo); - // $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $aac_thisfileinfo; - // if (isset($aac_thisfileinfo['audio']) && is_array($aac_thisfileinfo['audio'])) { - // foreach ($aac_thisfileinfo['audio'] as $key => $value) { - // $track_info[$key] = $value; - // } - // } - // unset($aac_thisfileinfo); - // unset($getid3_aac); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; + $getid3_aac = new getid3_aac($getid3_temp); + $getid3_aac->Analyze(); + unset($getid3_temp->info['aac']['GETID3_VERSION']); + if (!empty($getid3_temp->info['audio']['dataformat'])) { + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['aac']; + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { + $track_info[$key] = $value; + } + } + } else { + $this->getid3->warning('Failed to parse '.$trackarray[$this->EBMLidName(EBML_ID_CODECID)].' audio data ['.basename(__FILE__).':'.__LINE__.']'); + } + unset($getid3_temp, $getid3_aac); } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.aac.php"'; + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.aac.php"'); } break; case 'A_MPEG/L3': if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, false)) { - $mp3_thisfileinfo = array( - 'avdataoffset' => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']], - 'avdataend' => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']] + 1024, - ); - $getid3_mp3 = new getid3_mp3($fd, $mp3_thisfileinfo); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; + $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length']; + $getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3->allow_bruteforce = true; - //getid3_mp3::getOnlyMPEGaudioInfo($fd, $mp3_thisfileinfo, $offset, false); - $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $mp3_thisfileinfo; - if (!empty($mp3_thisfileinfo['error'])) { - foreach ($mp3_thisfileinfo['error'] as $newerror) { - $this->warnings[] = 'getid3_mp3() says: ['.$newerror.']'; + $getid3_mp3->Analyze(); + if (!empty($getid3_temp->info['mpeg'])) { + unset($getid3_temp->info['mpeg']['GETID3_VERSION']); + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['mpeg']; + if (!empty($getid3_temp->info['error'])) { + foreach ($getid3_temp->info['error'] as $newerror) { + $this->getid3->warning('getid3_mp3() says: ['.$newerror.']'); + } } - } - if (!empty($mp3_thisfileinfo['warning'])) { - foreach ($mp3_thisfileinfo['warning'] as $newerror) { - $this->warnings[] = 'getid3_mp3() says: ['.$newerror.']'; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->getid3->warning('getid3_mp3() says: ['.$newerror.']'); + } } - } - if (isset($mp3_thisfileinfo['audio']) && is_array($mp3_thisfileinfo['audio'])) { - foreach ($mp3_thisfileinfo['audio'] as $key => $value) { - $track_info[$key] = $value; + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { + $track_info[$key] = $value; + } } + } else { + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because getid3_mp3::Analyze failed at offset '.$info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']); } - unset($mp3_thisfileinfo); - unset($getid3_mp3); + unset($getid3_temp, $getid3_mp3); } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.mp3.php"'; + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.mp3.php"'); } break; @@ -1309,18 +1523,29 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems } if ($found_vorbis) { if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) { - $vorbis_fileinfo = array(); $oggpageinfo['page_seqno'] = 0; - getid3_ogg::ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $vorbis_fileinfo, $oggpageinfo); - $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $vorbis_fileinfo; + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_ogg = new getid3_ogg($getid3_temp); + $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo); + $vorbis_fileinfo = $getid3_temp->info; + unset($getid3_temp, $getid3_ogg); + + if (isset($vorbis_fileinfo['audio'])) { + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']]['audio'] = $vorbis_fileinfo['audio']; + } + if (isset($vorbis_fileinfo['ogg'])) { + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']]['ogg'] = $vorbis_fileinfo['ogg']; + } if (!empty($vorbis_fileinfo['error'])) { foreach ($vorbis_fileinfo['error'] as $newerror) { - $this->warnings[] = 'getid3_ogg() says: ['.$newerror.']'; + $this->getid3->warning('getid3_ogg() says: ['.$newerror.']'); } } if (!empty($vorbis_fileinfo['warning'])) { foreach ($vorbis_fileinfo['warning'] as $newerror) { - $this->warnings[] = 'getid3_ogg() says: ['.$newerror.']'; + $this->getid3->warning('getid3_ogg() says: ['.$newerror.']'); } } if (isset($vorbis_fileinfo['audio']) && is_array($vorbis_fileinfo['audio'])) { @@ -1336,7 +1561,7 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems unset($vorbis_fileinfo); unset($oggpageinfo); } else { - $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"'; + $this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"'); } } else { } @@ -1345,15 +1570,15 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems break; default: - $this->warnings[] = 'Unhandled audio type "'.(isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '').'"'; + $this->getid3->warning('Unhandled audio type "'.(isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '').'"'); break; } - $ThisFileInfo['audio']['streams'][] = $track_info; - if (isset($track_info['dataformat']) && empty($ThisFileInfo['audio']['dataformat'])) { + $info['audio']['streams'][] = $track_info; + if (isset($track_info['dataformat']) && empty($info['audio']['dataformat'])) { foreach ($track_info as $key => $value) { - $ThisFileInfo['audio'][$key] = $value; + $info['audio'][$key] = $value; } } break; @@ -1366,34 +1591,34 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems if ($this->hide_clusters) { // too much data returned that is usually not useful - if (isset($ThisFileInfo['matroska']['segments']) && is_array($ThisFileInfo['matroska']['segments'])) { - foreach ($ThisFileInfo['matroska']['segments'] as $key => $segmentsarray) { + if (isset($info['matroska']['segments']) && is_array($info['matroska']['segments'])) { + foreach ($info['matroska']['segments'] as $key => $segmentsarray) { if ($segmentsarray['id'] == EBML_ID_CLUSTER) { - unset($ThisFileInfo['matroska']['segments'][$key]); + unset($info['matroska']['segments'][$key]); } } } - if (isset($ThisFileInfo['matroska']['seek']) && is_array($ThisFileInfo['matroska']['seek'])) { - foreach ($ThisFileInfo['matroska']['seek'] as $key => $seekarray) { + if (isset($info['matroska']['seek']) && is_array($info['matroska']['seek'])) { + foreach ($info['matroska']['seek'] as $key => $seekarray) { if ($seekarray['target_id'] == EBML_ID_CLUSTER) { - unset($ThisFileInfo['matroska']['seek'][$key]); + unset($info['matroska']['seek'][$key]); } } } - //unset($ThisFileInfo['matroska']['cluster']); - //unset($ThisFileInfo['matroska']['track_data_offsets']); + //unset($info['matroska']['cluster']); + //unset($info['matroska']['track_data_offsets']); } - if (!empty($ThisFileInfo['video']['streams'])) { - $ThisFileInfo['mime_type'] = 'video/x-matroska'; - } elseif (!empty($ThisFileInfo['audio']['streams'])) { - $ThisFileInfo['mime_type'] = 'audio/x-matroska'; - } elseif (isset($ThisFileInfo['mime_type'])) { - unset($ThisFileInfo['mime_type']); + if (!empty($info['video']['streams'])) { + $info['mime_type'] = 'video/x-matroska'; + } elseif (!empty($info['audio']['streams'])) { + $info['mime_type'] = 'audio/x-matroska'; + } elseif (isset($info['mime_type'])) { + unset($info['mime_type']); } foreach ($this->warnings as $key => $value) { - $ThisFileInfo['warning'][] = $value; + $info['warning'][] = $value; } return true; @@ -1403,15 +1628,19 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems /////////////////////////////////////// - function EnsureBufferHasEnoughData(&$fd, &$EBMLdata, &$offset, &$EBMLdata_offset) { - $min_data = 1024; + function EnsureBufferHasEnoughData(&$EBMLdata, &$offset, &$EBMLdata_offset, $min_data=1024) { if (!getid3_lib::intValueSupported($offset + $this->read_buffer_size)) { - $offset = pow(2, 63); + $offset = PHP_INT_MAX + 1; return false; - } elseif (($offset - $EBMLdata_offset) >= (strlen($EBMLdata) - $min_data)) { - fseek($fd, $offset, SEEK_SET); - $EBMLdata_offset = ftell($fd); - $EBMLdata = fread($fd, $this->read_buffer_size); + } + if (($offset - $EBMLdata_offset) >= (strlen($EBMLdata) - $min_data)) { + fseek($this->getid3->fp, $offset, SEEK_SET); + $EBMLdata_offset = ftell($this->getid3->fp); + $EBMLdata = fread($this->getid3->fp, max($min_data, $this->read_buffer_size)); + if ((strlen($EBMLdata) == 0) && feof($this->getid3->fp)) { + $this->errors[] = 'EnsureBufferHasEnoughData() ran out of file at offset '.$offset; + return false; + } } return true; } @@ -1419,13 +1648,13 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems function readEBMLint(&$string, &$offset, $dataoffset=0) { $actual_offset = $offset - $dataoffset; if (!getid3_lib::intValueSupported($offset + $this->read_buffer_size)) { - $this->warnings[] = 'aborting readEBMLint() because $offset larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + $this->getid3->warning('aborting readEBMLint() because $offset larger than '.round(PHP_INT_MAX / 1073741824).'GB'); return false; } elseif ($actual_offset >= strlen($string)) { - $this->warnings[] = '$actual_offset > $string in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')'; + $this->getid3->warning('$actual_offset > $string in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')'); return false; } elseif ($actual_offset < 0) { - $this->warnings[] = '$actual_offset < 0 in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')'; + $this->getid3->warning('$actual_offset < 0 in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')'); return false; } $first_byte_int = ord($string{$actual_offset}); @@ -1446,8 +1675,8 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems } elseif (0x01 & $first_byte_int) { $length = 8; } else { - $offset = pow(2, 63); // abort processing, skip to end of file - $this->warnings[] = 'invalid EBML integer (leading 0x00) at '.$offset; + $this->getid3->warning('invalid EBML integer (leading 0x00) at '.$offset); + $offset = PHP_INT_MAX + 1; // abort processing, skip to end of file return false; } $int_value = $this->EBML2Int(substr($string, $actual_offset, $length)); @@ -1455,7 +1684,7 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems return $int_value; } - function EBML2Int($EBMLstring) { + static function EBML2Int($EBMLstring) { // http://matroska.org/specs/ // Element ID coded with an UTF-8 like system: @@ -1499,14 +1728,80 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems } - function EBMLdate2unix($EBMLdatestamp) { + 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); } - function MatroskaBlockLacingType($lacingtype) { + function ExtractCommentsSimpleTag($SimpleTagArray) { + $info = &$this->getid3->info; + if (!empty($SimpleTagArray[$this->EBMLidName(EBML_ID_SIMPLETAG)])) { + foreach ($SimpleTagArray[$this->EBMLidName(EBML_ID_SIMPLETAG)] as $SimpleTagKey => $SimpleTagData) { + if (!empty($SimpleTagData[$this->EBMLidName(EBML_ID_TAGNAME)]) && !empty($SimpleTagData[$this->EBMLidName(EBML_ID_TAGSTRING)])) { + $info['matroska']['comments'][strtolower($SimpleTagData[$this->EBMLidName(EBML_ID_TAGNAME)])][] = $SimpleTagData[$this->EBMLidName(EBML_ID_TAGSTRING)]; + } + if (!empty($SimpleTagData[$this->EBMLidName(EBML_ID_SIMPLETAG)])) { + $this->ExtractCommentsSimpleTag($SimpleTagData); + } + } + } + return true; + } + + function Handle_EMBL_ID_SIMPLETAG(&$offset, $sub_subelement_end) { + $simpletag_entry = array(); + while ($offset < $sub_subelement_end) { + if (!$this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset)) { + $this->getid3->error('EnsureBufferHasEnoughData() failed at offset '.$offset); + break; + } + $sub_sub_subelement_offset = $offset; + $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); + $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id); + $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset); + $sub_sub_subelement_end = $offset + $sub_sub_subelement_length; + switch ($sub_sub_subelement_id) { + case EBML_ID_TAGNAME: + case EBML_ID_TAGLANGUAGE: + case EBML_ID_TAGSTRING: + case EBML_ID_TAGBINARY: + $simpletag_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length); + break; + case EBML_ID_SIMPLETAG: + $simpletag_entry[$sub_sub_subelement_idname][] = $this->Handle_EMBL_ID_SIMPLETAG($offset, $sub_sub_subelement_end); + break; + case EBML_ID_TAGDEFAULT: + $simpletag_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length)); + break; + + default: + $this->getid3->warning('Unhandled tag.simpletag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset); + break; + } + $offset = $sub_sub_subelement_end; + } + return $simpletag_entry; + } + + + static function MatroskaTargetTypeValue($target_type) { + // http://www.matroska.org/technical/specs/tagging/index.html + static $MatroskaTargetTypeValue = array(); + if (empty($MatroskaTargetTypeValue)) { + $MatroskaTargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies + $MatroskaTargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement) + $MatroskaTargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie + $MatroskaTargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts + $MatroskaTargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series) + $MatroskaTargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together + $MatroskaTargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items + } + return (isset($MatroskaTargetTypeValue[$target_type]) ? $MatroskaTargetTypeValue[$target_type] : $target_type); + } + + static function MatroskaBlockLacingType($lacingtype) { // http://matroska.org/technical/specs/index.html#block_structure static $MatroskaBlockLacingType = array(); if (empty($MatroskaBlockLacingType)) { @@ -1518,7 +1813,7 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems return (isset($MatroskaBlockLacingType[$lacingtype]) ? $MatroskaBlockLacingType[$lacingtype] : $lacingtype); } - function MatroskaCodecIDtoCommonName($codecid) { + static function MatroskaCodecIDtoCommonName($codecid) { // http://www.matroska.org/technical/specs/codecid/index.html static $MatroskaCodecIDlist = array(); if (empty($MatroskaCodecIDlist)) { @@ -1550,7 +1845,7 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems return (isset($MatroskaCodecIDlist[$codecid]) ? $MatroskaCodecIDlist[$codecid] : $codecid); } - function EBMLidName($value) { + static function EBMLidName($value) { static $EBMLidList = array(); if (empty($EBMLidList)) { $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType'; @@ -1745,5 +2040,4 @@ $this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems } } - ?>
\ No newline at end of file diff --git a/modules/getid3/module.audio-video.mpeg.php b/modules/getid3/module.audio-video.mpeg.php index 8f487848..499b740c 100644 --- a/modules/getid3/module.audio-video.mpeg.php +++ b/modules/getid3/module.audio-video.mpeg.php @@ -25,17 +25,19 @@ define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8"); define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0"); -class getid3_mpeg +class getid3_mpeg extends getid3_handler { - function getid3_mpeg(&$fd, &$ThisFileInfo) { - if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) { - $ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')'; + function Analyze() { + $info = &$this->getid3->info; + + if ($info['avdataend'] <= $info['avdataoffset']) { + $info['error'][] = '"avdataend" ('.$info['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$info['avdataoffset'].')'; return false; } - $ThisFileInfo['fileformat'] = 'mpeg'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])); + $info['fileformat'] = 'mpeg'; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $MPEGstreamData = fread($this->getid3->fp, min(100000, $info['avdataend'] - $info['avdataoffset'])); $MPEGstreamDataLength = strlen($MPEGstreamData); $foundVideo = true; @@ -62,7 +64,7 @@ class getid3_mpeg // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) - $ThisFileInfo['video']['dataformat'] = 'mpeg'; + $info['video']['dataformat'] = 'mpeg'; $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1); @@ -75,71 +77,71 @@ class getid3_mpeg $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4)); $VideoChunkOffset += 4; - $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size - $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size - $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4; - $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F); + $info['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size + $info['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size + $info['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4; + $info['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F); - $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; - $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; + $info['mpeg']['video']['framesize_horizontal'] = $info['mpeg']['video']['raw']['framesize_horizontal']; + $info['mpeg']['video']['framesize_vertical'] = $info['mpeg']['video']['raw']['framesize_vertical']; - $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); - $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); - $ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); + $info['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']); + $info['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']); + $info['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($info['mpeg']['video']['raw']['frame_rate']); - $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18)); - $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1)); - $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10)); - $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1)); - $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1)); - if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) { + $info['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18)); + $info['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1)); + $info['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10)); + $info['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1)); + $info['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1)); + if ($info['mpeg']['video']['raw']['intra_quant_flag']) { // read 512 bits - $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)); + $info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)); $VideoChunkOffset += 64; - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1)); - $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511); + $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($info['mpeg']['video']['raw']['intra_quant'], 511, 1)); + $info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511); - if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); + if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) { + $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); $VideoChunkOffset += 64; } } else { - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)); - if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); + $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)); + if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) { + $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); $VideoChunkOffset += 64; } } - if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits + if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits - $ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files'; - $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr'; + $info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files'; + $info['mpeg']['video']['bitrate_mode'] = 'vbr'; } else { - $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; - $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; + $info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400; + $info['mpeg']['video']['bitrate_mode'] = 'cbr'; + $info['video']['bitrate'] = $info['mpeg']['video']['bitrate']; } - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; - $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; - $ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio']; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['bits_per_sample'] = 24; + $info['video']['resolution_x'] = $info['mpeg']['video']['framesize_horizontal']; + $info['video']['resolution_y'] = $info['mpeg']['video']['framesize_vertical']; + $info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate']; + $info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode']; + $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio']; + $info['video']['lossless'] = false; + $info['video']['bits_per_sample'] = 24; } else { - $ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; + $info['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; } @@ -151,9 +153,9 @@ class getid3_mpeg //difference between MPEG-1 and MPEG-2 video streams. if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) { - $ThisFileInfo['video']['codec'] = 'MPEG-2'; + $info['video']['codec'] = 'MPEG-2'; } else { - $ThisFileInfo['video']['codec'] = 'MPEG-1'; + $info['video']['codec'] = 'MPEG-1'; } @@ -165,33 +167,38 @@ class getid3_mpeg } } + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info = $info; + $getid3_mp3 = new getid3_mp3($getid3_temp); for ($i = 0; $i <= 7; $i++) { // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after // I have no idea why or what the difference is, so this is a stupid hack. // If anybody has any better idea of what's going on, please let me know - info@getid3.org - - $dummy = $ThisFileInfo; - if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) { - $ThisFileInfo = $dummy; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; + fseek($getid3_temp->fp, ftell($this->getid3->fp), SEEK_SET); + $getid3_temp->info = $info; // only overwrite real data if valid header found + if ($getid3_mp3->decodeMPEGaudioHeader(($AudioChunkOffset + 3) + 8 + $i, $getid3_temp->info, false)) { + $info = $getid3_temp->info; + $info['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['lossless'] = false; + unset($getid3_temp, $getid3_mp3); break 2; - } } + unset($getid3_temp, $getid3_mp3); } // Temporary hack to account for interleaving overhead: - if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) { - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']); + if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) { + $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']); // Interleaved MPEG audio/video files have a certain amount of overhead that varies // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter // Use interpolated lookup tables to approximately guess how much is overhead, because // playtime is calculated as filesize / total-bitrate - $ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']); + $info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']); - //switch ($ThisFileInfo['video']['bitrate']) { + //switch ($info['video']['bitrate']) { // case('5000000'): // $multiplier = 0.93292642112380355828048824319889; // break; @@ -208,10 +215,10 @@ class getid3_mpeg // $multiplier = 1; // break; //} - //$ThisFileInfo['playtime_seconds'] *= $multiplier; - //$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'; - if ($ThisFileInfo['video']['bitrate'] < 50000) { - $ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'; + //$info['playtime_seconds'] *= $multiplier; + //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'; + if ($info['video']['bitrate'] < 50000) { + $info['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'; } } diff --git a/modules/getid3/module.audio-video.nsv.php b/modules/getid3/module.audio-video.nsv.php index dab03389..5a587e67 100644 --- a/modules/getid3/module.audio-video.nsv.php +++ b/modules/getid3/module.audio-video.nsv.php @@ -14,199 +14,201 @@ ///////////////////////////////////////////////////////////////// -class getid3_nsv +class getid3_nsv extends getid3_handler { - function getid3_nsv(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $NSVheader = fread($fd, 4); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $NSVheader = fread($this->getid3->fp, 4); switch ($NSVheader) { case 'NSVs': - if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) { - $ThisFileInfo['fileformat'] = 'nsv'; - $ThisFileInfo['audio']['dataformat'] = 'nsv'; - $ThisFileInfo['video']['dataformat'] = 'nsv'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['video']['lossless'] = false; + if ($this->getNSVsHeaderFilepointer(0)) { + $info['fileformat'] = 'nsv'; + $info['audio']['dataformat'] = 'nsv'; + $info['video']['dataformat'] = 'nsv'; + $info['audio']['lossless'] = false; + $info['video']['lossless'] = false; } break; case 'NSVf': - if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) { - $ThisFileInfo['fileformat'] = 'nsv'; - $ThisFileInfo['audio']['dataformat'] = 'nsv'; - $ThisFileInfo['video']['dataformat'] = 'nsv'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['video']['lossless'] = false; - $this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']); + if ($this->getNSVfHeaderFilepointer(0)) { + $info['fileformat'] = 'nsv'; + $info['audio']['dataformat'] = 'nsv'; + $info['video']['dataformat'] = 'nsv'; + $info['audio']['lossless'] = false; + $info['video']['lossless'] = false; + $this->getNSVsHeaderFilepointer($info['nsv']['NSVf']['header_length']); } break; default: - $ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"'; + $info['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"'; return false; break; } - if (!isset($ThisFileInfo['nsv']['NSVf'])) { - $ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate'; + if (!isset($info['nsv']['NSVf'])) { + $info['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate'; } return true; } - function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) { - fseek($fd, $fileoffset, SEEK_SET); - $NSVsheader = fread($fd, 28); + function getNSVsHeaderFilepointer($fileoffset) { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $fileoffset, SEEK_SET); + $NSVsheader = fread($this->getid3->fp, 28); $offset = 0; - $ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4); + $info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4); $offset += 4; - if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') { - $ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead'; - unset($ThisFileInfo['nsv']['NSVs']); + if ($info['nsv']['NSVs']['identifier'] != 'NSVs') { + $info['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVs']['identifier'].'" instead'; + unset($info['nsv']['NSVs']); return false; } - $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; + $info['nsv']['NSVs']['offset'] = $fileoffset; - $ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4); + $info['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4); $offset += 4; - $ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4); + $info['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4); $offset += 4; - $ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $info['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); $offset += 2; - $ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $info['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); $offset += 2; - $ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $info['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + //$info['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) { + switch ($info['nsv']['NSVs']['audio_codec']) { case 'PCM ': - $ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $info['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - $ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $info['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); $offset += 1; - $ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $info['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); $offset += 2; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate']; + $info['audio']['sample_rate'] = $info['nsv']['NSVs']['sample_rate']; break; case 'MP3 ': case 'NONE': default: - //$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4)); + //$info['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4)); $offset += 4; break; } - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y']; - $ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']); - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate']; - $ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $info['video']['resolution_x'] = $info['nsv']['NSVs']['resolution_x']; + $info['video']['resolution_y'] = $info['nsv']['NSVs']['resolution_y']; + $info['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($info['nsv']['NSVs']['framerate_index']); + $info['video']['frame_rate'] = $info['nsv']['NSVs']['frame_rate']; + $info['video']['bits_per_sample'] = 24; + $info['video']['pixel_aspect_ratio'] = (float) 1; return true; } - function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) { - fseek($fd, $fileoffset, SEEK_SET); - $NSVfheader = fread($fd, 28); + function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $fileoffset, SEEK_SET); + $NSVfheader = fread($this->getid3->fp, 28); $offset = 0; - $ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); + $info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); $offset += 4; - if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') { - $ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead'; - unset($ThisFileInfo['nsv']['NSVf']); + if ($info['nsv']['NSVf']['identifier'] != 'NSVf') { + $info['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVf']['identifier'].'" instead'; + unset($info['nsv']['NSVf']); return false; } - $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; + $info['nsv']['NSVs']['offset'] = $fileoffset; - $ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - $ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes'; + if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) { + $info['warning'][] = 'truncated file - NSVf header indicates '.$info['nsv']['NSVf']['file_size'].' bytes, file actually '.$info['avdataend'].' bytes'; } - $ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - $ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - $ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $info['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; - if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero'; + if ($info['nsv']['NSVf']['playtime_ms'] == 0) { + $info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero'; return false; } - $NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'])); + $NSVfheader .= fread($this->getid3->fp, $info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2'])); $NSVfheaderlength = strlen($NSVfheader); - $ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']); - $offset += $ThisFileInfo['nsv']['NSVf']['meta_size']; + $info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']); + $offset += $info['nsv']['NSVf']['meta_size']; if ($getTOCoffsets) { $TOCcounter = 0; - while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { - if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { - $ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) { + if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) { + $info['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $TOCcounter++; } } } - if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') { - $ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']); - $CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']); + if (trim($info['nsv']['NSVf']['metadata']) != '') { + $info['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $info['nsv']['NSVf']['metadata']); + $CommentPairArray = explode("\x01".' ', $info['nsv']['NSVf']['metadata']); foreach ($CommentPairArray as $CommentPair) { if (strstr($CommentPair, '='."\x01")) { list($key, $value) = explode('='."\x01", $CommentPair, 2); - $ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value)); + $info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value)); } } } - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000; - $ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000; + $info['bitrate'] = ($info['nsv']['NSVf']['file_size'] * 8) / $info['playtime_seconds']; return true; } - function NSVframerateLookup($framerateindex) { + static function NSVframerateLookup($framerateindex) { if ($framerateindex <= 127) { return (float) $framerateindex; } - static $NSVframerateLookup = array(); if (empty($NSVframerateLookup)) { $NSVframerateLookup[129] = (float) 29.970; diff --git a/modules/getid3/module.audio-video.quicktime.php b/modules/getid3/module.audio-video.quicktime.php index ebcb45e7..2cc265c6 100644 --- a/modules/getid3/module.audio-video.quicktime.php +++ b/modules/getid3/module.audio-video.quicktime.php @@ -15,42 +15,46 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); -class getid3_quicktime +class getid3_quicktime extends getid3_handler { - function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData=true, $ParseAllPossibleAtoms=false) { + var $ReturnAtomData = true; + var $ParseAllPossibleAtoms = false; - $ThisFileInfo['fileformat'] = 'quicktime'; - $ThisFileInfo['quicktime']['hinting'] = false; - $ThisFileInfo['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $info['fileformat'] = 'quicktime'; + $info['quicktime']['hinting'] = false; + $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $offset = 0; $atomcounter = 0; - while ($offset < $ThisFileInfo['avdataend']) { + while ($offset < $info['avdataend']) { if (!getid3_lib::intValueSupported($offset)) { - $ThisFileInfo['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; break; } - fseek($fd, $offset, SEEK_SET); - $AtomHeader = fread($fd, 8); + fseek($this->getid3->fp, $offset, SEEK_SET); + $AtomHeader = fread($this->getid3->fp, 8); $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); $atomname = substr($AtomHeader, 4, 4); // 64-bit MOV patch by jlegateŘktnc*com if ($atomsize == 1) { - $atomsize = getid3_lib::BigEndian2Int(fread($fd, 8)); + $atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8)); } - $ThisFileInfo['quicktime'][$atomname]['name'] = $atomname; - $ThisFileInfo['quicktime'][$atomname]['size'] = $atomsize; - $ThisFileInfo['quicktime'][$atomname]['offset'] = $offset; + $info['quicktime'][$atomname]['name'] = $atomname; + $info['quicktime'][$atomname]['size'] = $atomsize; + $info['quicktime'][$atomname]['offset'] = $offset; - if (($offset + $atomsize) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'; + if (($offset + $atomsize) > $info['avdataend']) { + $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'; return false; } @@ -63,25 +67,39 @@ class getid3_quicktime switch ($atomname) { case 'mdat': // Media DATa atom // 'mdat' contains the actual data for the audio/video - if (($atomsize > 8) && (!isset($ThisFileInfo['avdataend_tmp']) || ($ThisFileInfo['quicktime'][$atomname]['size'] > ($ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])))) { - - $ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8; - $OldAVDataEnd = $ThisFileInfo['avdataend']; - $ThisFileInfo['avdataend'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size']; - - if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) { - getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false); - if (isset($ThisFileInfo['mpeg']['audio'])) { - $ThisFileInfo['audio']['dataformat'] = 'mp3'; - $ThisFileInfo['audio']['codec'] = (!empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; + if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { + + $info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8; + $OldAVDataEnd = $info['avdataend']; + $info['avdataend'] = $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(fread($this->getid3->fp, 4)))) { + $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $value) { + $info['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']; + } } } - $ThisFileInfo['avdataend'] = $OldAVDataEnd; + unset($getid3_mp3, $getid3_temp); + $info['avdataend'] = $OldAVDataEnd; unset($OldAVDataEnd); } @@ -95,7 +113,7 @@ class getid3_quicktime default: $atomHierarchy = array(); - $ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms); + $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); break; } @@ -103,59 +121,63 @@ class getid3_quicktime $atomcounter++; } - if (!empty($ThisFileInfo['avdataend_tmp'])) { + if (!empty($info['avdataend_tmp'])) { // this value is assigned to a temp value and then erased because // otherwise any atoms beyond the 'mdat' atom would not get parsed - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp']; - unset($ThisFileInfo['avdataend_tmp']); + $info['avdataend'] = $info['avdataend_tmp']; + unset($info['avdataend_tmp']); } - if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) { + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } - if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate']; + if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) { + $info['audio']['bitrate'] = $info['bitrate']; } - if (!empty($ThisFileInfo['playtime_seconds']) && !isset($ThisFileInfo['video']['frame_rate']) && !empty($ThisFileInfo['quicktime']['stts_framecount'])) { - foreach ($ThisFileInfo['quicktime']['stts_framecount'] as $key => $samples_count) { - $samples_per_second = $samples_count / $ThisFileInfo['playtime_seconds']; + 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']; if ($samples_per_second > 240) { // has to be audio samples } else { - $ThisFileInfo['video']['frame_rate'] = $samples_per_second; + $info['video']['frame_rate'] = $samples_per_second; break; } } } - if (($ThisFileInfo['audio']['dataformat'] == 'mp4') && empty($ThisFileInfo['video']['resolution_x'])) { - $ThisFileInfo['fileformat'] = 'mp4'; - $ThisFileInfo['mime_type'] = 'audio/mp4'; - unset($ThisFileInfo['video']['dataformat']); + if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) { + $info['fileformat'] = 'mp4'; + $info['mime_type'] = 'audio/mp4'; + unset($info['video']['dataformat']); } - if (!$ReturnAtomData) { - unset($ThisFileInfo['quicktime']['moov']); + if (!$this->ReturnAtomData) { + unset($info['quicktime']['moov']); } - if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) { - $ThisFileInfo['audio']['dataformat'] = 'quicktime'; + if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) { + $info['audio']['dataformat'] = 'quicktime'; } - if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) { - $ThisFileInfo['video']['dataformat'] = 'quicktime'; + if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) { + $info['video']['dataformat'] = 'quicktime'; } return true; } - function QuicktimeParseAtom($atomname, $atomsize, $atom_data, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { + function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm - $atomparent = array_pop($atomHierarchy); + $info = &$this->getid3->info; + + $atom_parent = array_pop($atomHierarchy); array_push($atomHierarchy, $atomname); $atom_structure['hierarchy'] = implode(' ', $atomHierarchy); $atom_structure['name'] = $atomname; $atom_structure['size'] = $atomsize; $atom_structure['offset'] = $baseoffset; +//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8)).'<br>'; +//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8), false).'<br><br>'; switch ($atomname) { case 'moov': // MOVie container atom case 'trak': // TRAcK container atom @@ -171,12 +193,46 @@ class getid3_quicktime 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 - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + $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(); + 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; case 'stbl': // Sample TaBLe container atom - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); $isVideo = false; $framerate = 0; $framecount = 0; @@ -199,18 +255,18 @@ class getid3_quicktime } 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($ThisFileInfo['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); + $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); $framecount = $value_array2['sample_count']; } } } } if ($isVideo && $framerate) { - $ThisFileInfo['quicktime']['video']['frame_rate'] = $framerate; - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['quicktime']['video']['frame_rate']; + $info['quicktime']['video']['frame_rate'] = $framerate; + $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; } if ($isVideo && $framecount) { - $ThisFileInfo['quicktime']['video']['frame_count'] = $framecount; + $info['quicktime']['video']['frame_count'] = $framecount; } break; @@ -290,110 +346,130 @@ class getid3_quicktime case '©wrn': case '©wrt': // WRiTer case '----': // itunes specific - if ($atomparent == 'udta') { + 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['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($ThisFileInfo['comments']['language']) || (!in_array($atom_structure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atom_structure['language']; + 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; - 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); - - 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 'pcst': - case 'pgap': - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - break; - - case 'tmpo': - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); - break; - - case 'disk': - case 'trkn': - $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': - $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); - $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); - break; - - case 'rtng': - $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); - break; - - case 'stik': - $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); - $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); - break; - - case 'sfID': - $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; - - default: - $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); - } - break; + 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); + switch ($boxsmalltype) { + case "\x10\xB5": + $atom_structure['data'] = $boxsmalldata; + break; + default: + $info['warning'][] = 'Unknown QuickTime smallbox type: "'.$boxsmalltype.'" at offset '.$baseoffset; + $atom_structure['data'] = $atom_data; + break; + } + $atomoffset += (4 + $boxsmallsize); + } + } 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); + + switch ($boxtype) { + case 'mean': + case 'name': + $atom_structure[$boxtype] = substr($boxdata, 4); + break; - case 1: // text flag - case 13: // image flag - default: - $atom_structure['data'] = substr($boxdata, 8); - 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 'pcst': + case 'pgap': + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + break; + + case 'tmpo': + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); + break; + + case 'disk': + case 'trkn': + $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': + $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); + break; + + case 'rtng': + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); + break; + + case 'stik': + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); + break; + + case 'sfID': + $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; + + default: + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + } + break; - } - break; + case 1: // text flag + case 13: // image flag + default: + $atom_structure['data'] = substr($boxdata, 8); + break; - default: - $ThisFileInfo['warning'][] = 'Unknown QuickTime box type: "'.$boxtype.'" at offset '.$baseoffset; - $atom_structure['data'] = $atom_data; + } + break; + + default: + $info['warning'][] = 'Unknown QuickTime box type: "'.$boxtype.'" at offset '.$baseoffset; + $atom_structure['data'] = $atom_data; + } + $atomoffset += $boxsize; } - $atomoffset += $boxsize; } } - $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $ThisFileInfo, $atom_structure['name']); + $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)); + $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); - $ThisFileInfo['quicktime']['autoplay'] = $atom_structure['autoplay']; + $info['quicktime']['autoplay'] = $atom_structure['autoplay']; break; @@ -424,14 +500,10 @@ class getid3_quicktime $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); $CompressedFileData = substr($atom_data, 4); - ob_start(); - if ($UncompressedHeader = gzuncompress($CompressedFileData)) { - ob_end_clean(); - $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, $ThisFileInfo, 0, $atomHierarchy, $ParseAllPossibleAtoms); + if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $ThisFileInfo['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset']; + $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset']; } break; @@ -518,8 +590,8 @@ class getid3_quicktime $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); - if (empty($ThisFileInfo['comments']['language']) || (!in_array($atom_structure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atom_structure['language']; + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; } break; @@ -550,7 +622,7 @@ class getid3_quicktime if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; } else { - $ThisFileInfo['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'; + $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'; } break; @@ -589,32 +661,32 @@ class getid3_quicktime switch ($atom_structure['sample_description_table'][$i]['data_format']) { case 'avc1': case 'mp4v': - $ThisFileInfo['fileformat'] = 'mp4'; - $ThisFileInfo['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; - //$ThisFileInfo['warning'][] = 'This version of getID3() [v'.GETID3_VERSION.'] does not fully support MPEG-4 audio/video streams'; // 2011-02-18: why am I warning about this again? What's not supported? + $info['fileformat'] = 'mp4'; + $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; + //$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] does not fully support MPEG-4 audio/video streams'; // 2011-02-18: why am I warning about this again? What's not supported? break; case 'qtvr': - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + $info['video']['dataformat'] = 'quicktimevr'; break; case 'mp4a': default: - $ThisFileInfo['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); - $ThisFileInfo['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; - $ThisFileInfo['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; - $ThisFileInfo['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; - $ThisFileInfo['audio']['codec'] = $ThisFileInfo['quicktime']['audio']['codec']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['quicktime']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['quicktime']['audio']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['quicktime']['audio']['bit_depth']; + $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 - $ThisFileInfo['audio']['lossless'] = true; + $info['audio']['lossless'] = true; break; default: - $ThisFileInfo['audio']['lossless'] = false; + $info['audio']['lossless'] = false; break; } break; @@ -624,7 +696,7 @@ class getid3_quicktime default: switch ($atom_structure['sample_description_table'][$i]['data_format']) { case 'mp4s': - $ThisFileInfo['fileformat'] = 'mp4'; + $info['fileformat'] = 'mp4'; break; default: @@ -646,39 +718,39 @@ class getid3_quicktime $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') { - $ThisFileInfo['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; - $ThisFileInfo['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); - $ThisFileInfo['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']); - $ThisFileInfo['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; - $ThisFileInfo['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; - - $ThisFileInfo['video']['codec'] = $ThisFileInfo['quicktime']['video']['codec']; - $ThisFileInfo['video']['bits_per_sample'] = $ThisFileInfo['quicktime']['video']['color_depth']; + $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']; } - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $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': - $ThisFileInfo['audio']['dataformat'] = 'mp4'; - $ThisFileInfo['quicktime']['audio']['codec'] = 'mp4'; + $info['audio']['dataformat'] = 'mp4'; + $info['quicktime']['audio']['codec'] = 'mp4'; break; case '3ivx': case '3iv1': case '3iv2': - $ThisFileInfo['video']['dataformat'] = '3ivx'; + $info['video']['dataformat'] = '3ivx'; break; case 'xvid': - $ThisFileInfo['video']['dataformat'] = 'xvid'; + $info['video']['dataformat'] = 'xvid'; break; case 'mp4v': - $ThisFileInfo['video']['dataformat'] = 'mpeg4'; + $info['video']['dataformat'] = 'mpeg4'; break; case 'divx': @@ -688,7 +760,7 @@ class getid3_quicktime case 'div4': case 'div5': case 'div6': - $TDIVXileInfo['video']['dataformat'] = 'divx'; + $info['video']['dataformat'] = 'divx'; break; default: @@ -716,17 +788,17 @@ class getid3_quicktime $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM - //if (!empty($ThisFileInfo['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { - // $stts_new_framerate = $ThisFileInfo['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; + //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 - // $ThisFileInfo['video']['frame_rate'] = max($ThisFileInfo['video']['frame_rate'], $stts_new_framerate); + // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); // } //} // - //$FrameRateCalculatorArray[($ThisFileInfo['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; + //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; } - $ThisFileInfo['quicktime']['stts_framecount'][] = $frames_count; + $info['quicktime']['stts_framecount'][] = $frames_count; //$sttsFramesTotal = 0; //$sttsSecondsTotal = 0; //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { @@ -740,8 +812,8 @@ class getid3_quicktime // $sttsSecondsTotal += $frame_count / $frames_per_second; //} //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { - // if (($sttsFramesTotal / $sttsSecondsTotal) > $ThisFileInfo['video']['frame_rate']) { - // $ThisFileInfo['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; + // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { + // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; // } //} break; @@ -889,7 +961,7 @@ class getid3_quicktime $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + $info['video']['dataformat'] = 'quicktimevr'; } break; @@ -905,17 +977,17 @@ class getid3_quicktime $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); if ($atom_structure['time_scale'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; + $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; return false; } - $ThisFileInfo['quicktime']['time_scale'] = (isset($ThisFileInfo['quicktime']['time_scale']) ? max($ThisFileInfo['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? 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($ThisFileInfo['comments']['language']) || (!in_array($atom_structure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atom_structure['language']; + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; } break; @@ -1019,14 +1091,14 @@ class getid3_quicktime $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); if ($atom_structure['time_scale'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero'; + $info['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']); - $ThisFileInfo['quicktime']['time_scale'] = (isset($ThisFileInfo['quicktime']['time_scale']) ? max($ThisFileInfo['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); - $ThisFileInfo['quicktime']['display_scale'] = $atom_structure['matrix_a']; - $ThisFileInfo['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; + $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? 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; @@ -1063,18 +1135,18 @@ class getid3_quicktime $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); if ($atom_structure['flags']['enabled'] == 1) { - if (!isset($ThisFileInfo['video']['resolution_x']) || !isset($ThisFileInfo['video']['resolution_y'])) { - $ThisFileInfo['video']['resolution_x'] = $atom_structure['width']; - $ThisFileInfo['video']['resolution_y'] = $atom_structure['height']; + 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']; } - $ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atom_structure['width']); - $ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atom_structure['height']); - $ThisFileInfo['quicktime']['video']['resolution_x'] = $ThisFileInfo['video']['resolution_x']; - $ThisFileInfo['quicktime']['video']['resolution_y'] = $ThisFileInfo['video']['resolution_y']; + $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 { - if (isset($ThisFileInfo['video']['resolution_x'])) { unset($ThisFileInfo['video']['resolution_x']); } - if (isset($ThisFileInfo['video']['resolution_y'])) { unset($ThisFileInfo['video']['resolution_y']); } - if (isset($ThisFileInfo['quicktime']['video'])) { unset($ThisFileInfo['quicktime']['video']); } + 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; @@ -1118,28 +1190,6 @@ class getid3_quicktime $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); break; - case 'meta': // METAdata atom - // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt - /* - $NextTagPosition = strpos($atom_data, '©'); - while ($NextTagPosition < strlen($atom_data)) { - $metaItemSize = getid3_lib::BigEndian2Int(substr($atom_data, $NextTagPosition - 4, 4)) - 4; - if ($metaItemSize == -4) { - break; - } - $metaItemRaw = substr($atom_data, $NextTagPosition, $metaItemSize); - $metaItemKey = substr($metaItemRaw, 0, 4); - $metaItemData = substr($metaItemRaw, 20); - $NextTagPosition += $metaItemSize + 4; - - $this->CopyToAppropriateCommentsSection($metaItemKey, $metaItemData, $ThisFileInfo); - } - */ - $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), $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; - 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)); @@ -1174,10 +1224,10 @@ class getid3_quicktime // 0x00 + 'std' for linear movie // 'none' for no controls $atom_structure['ctyp'] = substr($atom_data, 0, 4); - $ThisFileInfo['quicktime']['controller'] = $atom_structure['ctyp']; + $info['quicktime']['controller'] = $atom_structure['ctyp']; switch ($atom_structure['ctyp']) { case 'qtvr': - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + $info['video']['dataformat'] = 'quicktimevr'; break; } break; @@ -1190,7 +1240,7 @@ class getid3_quicktime case 'hinf': // case 'hinv': // case 'hnti': // - $ThisFileInfo['quicktime']['hinting'] = true; + $info['quicktime']['hinting'] = true; break; case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) @@ -1216,8 +1266,69 @@ class getid3_quicktime //$atom_structure['data'] = $atom_data; break; + case '©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 { + $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'; + } + 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)) { + $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 'NCHD': // MakerNoteVersion + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + $atom_structure['data'] = $atom_data; + break; + case 'NCTG': // NikonTags + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG + $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); + break; + case 'NCDB': // NikonTags + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + $atom_structure['data'] = $atom_data; + break; + + case "\x00\x00\x00\x00": + case 'meta': // METAdata atom + // some kind of metacontainer, may contain a big data dump such as: + // mdta keys mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst data DEApple 0 (data DE2011-05-11T17:54:04+0200 2 *data DE+52.4936+013.3897+040.247/ data DE4.3.1 data DEiPhone 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; + + case 'data': // metaDATA atom + // 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); + break; + default: - $ThisFileInfo['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" at offset '.$baseoffset; + $info['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" at offset '.$baseoffset; $atom_structure['data'] = $atom_data; break; } @@ -1225,7 +1336,8 @@ class getid3_quicktime return $atom_structure; } - function QuicktimeParseContainerAtom($atom_data, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { + function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { +//echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>'; $atom_structure = false; $subatomoffset = 0; $subatomcounter = 0; @@ -1234,8 +1346,8 @@ class getid3_quicktime } while ($subatomoffset < strlen($atom_data)) { $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4)); - $subatomname = substr($atom_data, $subatomoffset + 4, 4); - $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8); + $subatomname = substr($atom_data, $subatomoffset + 4, 4); + $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8); if ($subatomsize == 0) { // Furthermore, for historical reasons the list of atoms is optionally // terminated by a 32-bit integer set to 0. If you are writing a program @@ -1243,7 +1355,7 @@ class getid3_quicktime return $atom_structure; } - $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $ThisFileInfo, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); + $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); $subatomoffset += $subatomsize; $subatomcounter++; @@ -1533,56 +1645,56 @@ class getid3_quicktime static $QuicktimeIODSaudioProfileNameLookup = array(); if (empty($QuicktimeIODSaudioProfileNameLookup)) { $QuicktimeIODSaudioProfileNameLookup = array( - 0x00 => 'ISO Reserved (0x00)',
- 0x01 => 'Main Audio Profile @ Level 1',
- 0x02 => 'Main Audio Profile @ Level 2',
- 0x03 => 'Main Audio Profile @ Level 3',
- 0x04 => 'Main Audio Profile @ Level 4',
- 0x05 => 'Scalable Audio Profile @ Level 1',
- 0x06 => 'Scalable Audio Profile @ Level 2',
- 0x07 => 'Scalable Audio Profile @ Level 3',
- 0x08 => 'Scalable Audio Profile @ Level 4',
- 0x09 => 'Speech Audio Profile @ Level 1',
- 0x0A => 'Speech Audio Profile @ Level 2',
- 0x0B => 'Synthetic Audio Profile @ Level 1',
- 0x0C => 'Synthetic Audio Profile @ Level 2',
- 0x0D => 'Synthetic Audio Profile @ Level 3',
- 0x0E => 'High Quality Audio Profile @ Level 1',
- 0x0F => 'High Quality Audio Profile @ Level 2',
- 0x10 => 'High Quality Audio Profile @ Level 3',
- 0x11 => 'High Quality Audio Profile @ Level 4',
- 0x12 => 'High Quality Audio Profile @ Level 5',
- 0x13 => 'High Quality Audio Profile @ Level 6',
- 0x14 => 'High Quality Audio Profile @ Level 7',
- 0x15 => 'High Quality Audio Profile @ Level 8',
- 0x16 => 'Low Delay Audio Profile @ Level 1',
- 0x17 => 'Low Delay Audio Profile @ Level 2',
- 0x18 => 'Low Delay Audio Profile @ Level 3',
- 0x19 => 'Low Delay Audio Profile @ Level 4',
- 0x1A => 'Low Delay Audio Profile @ Level 5',
- 0x1B => 'Low Delay Audio Profile @ Level 6',
- 0x1C => 'Low Delay Audio Profile @ Level 7',
- 0x1D => 'Low Delay Audio Profile @ Level 8',
- 0x1E => 'Natural Audio Profile @ Level 1',
- 0x1F => 'Natural Audio Profile @ Level 2',
- 0x20 => 'Natural Audio Profile @ Level 3',
- 0x21 => 'Natural Audio Profile @ Level 4',
- 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
- 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
- 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
- 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
- 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
- 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
- 0x28 => 'AAC Profile @ Level 1',
- 0x29 => 'AAC Profile @ Level 2',
- 0x2A => 'AAC Profile @ Level 4',
- 0x2B => 'AAC Profile @ Level 5',
- 0x2C => 'High Efficiency AAC Profile @ Level 2',
- 0x2D => 'High Efficiency AAC Profile @ Level 3',
- 0x2E => 'High Efficiency AAC Profile @ Level 4',
- 0x2F => 'High Efficiency AAC Profile @ Level 5',
- 0xFE => 'Not part of MPEG-4 audio profiles',
- 0xFF => 'No audio capability required',
+ 0x00 => 'ISO Reserved (0x00)', + 0x01 => 'Main Audio Profile @ Level 1', + 0x02 => 'Main Audio Profile @ Level 2', + 0x03 => 'Main Audio Profile @ Level 3', + 0x04 => 'Main Audio Profile @ Level 4', + 0x05 => 'Scalable Audio Profile @ Level 1', + 0x06 => 'Scalable Audio Profile @ Level 2', + 0x07 => 'Scalable Audio Profile @ Level 3', + 0x08 => 'Scalable Audio Profile @ Level 4', + 0x09 => 'Speech Audio Profile @ Level 1', + 0x0A => 'Speech Audio Profile @ Level 2', + 0x0B => 'Synthetic Audio Profile @ Level 1', + 0x0C => 'Synthetic Audio Profile @ Level 2', + 0x0D => 'Synthetic Audio Profile @ Level 3', + 0x0E => 'High Quality Audio Profile @ Level 1', + 0x0F => 'High Quality Audio Profile @ Level 2', + 0x10 => 'High Quality Audio Profile @ Level 3', + 0x11 => 'High Quality Audio Profile @ Level 4', + 0x12 => 'High Quality Audio Profile @ Level 5', + 0x13 => 'High Quality Audio Profile @ Level 6', + 0x14 => 'High Quality Audio Profile @ Level 7', + 0x15 => 'High Quality Audio Profile @ Level 8', + 0x16 => 'Low Delay Audio Profile @ Level 1', + 0x17 => 'Low Delay Audio Profile @ Level 2', + 0x18 => 'Low Delay Audio Profile @ Level 3', + 0x19 => 'Low Delay Audio Profile @ Level 4', + 0x1A => 'Low Delay Audio Profile @ Level 5', + 0x1B => 'Low Delay Audio Profile @ Level 6', + 0x1C => 'Low Delay Audio Profile @ Level 7', + 0x1D => 'Low Delay Audio Profile @ Level 8', + 0x1E => 'Natural Audio Profile @ Level 1', + 0x1F => 'Natural Audio Profile @ Level 2', + 0x20 => 'Natural Audio Profile @ Level 3', + 0x21 => 'Natural Audio Profile @ Level 4', + 0x22 => 'Mobile Audio Internetworking Profile @ Level 1', + 0x23 => 'Mobile Audio Internetworking Profile @ Level 2', + 0x24 => 'Mobile Audio Internetworking Profile @ Level 3', + 0x25 => 'Mobile Audio Internetworking Profile @ Level 4', + 0x26 => 'Mobile Audio Internetworking Profile @ Level 5', + 0x27 => 'Mobile Audio Internetworking Profile @ Level 6', + 0x28 => 'AAC Profile @ Level 1', + 0x29 => 'AAC Profile @ Level 2', + 0x2A => 'AAC Profile @ Level 4', + 0x2B => 'AAC Profile @ Level 5', + 0x2C => 'High Efficiency AAC Profile @ Level 2', + 0x2D => 'High Efficiency AAC Profile @ Level 3', + 0x2E => 'High Efficiency AAC Profile @ Level 4', + 0x2F => 'High Efficiency AAC Profile @ Level 5', + 0xFE => 'Not part of MPEG-4 audio profiles', + 0xFF => 'No audio capability required', ); } return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private'); @@ -1593,68 +1705,68 @@ class getid3_quicktime static $QuicktimeIODSvideoProfileNameLookup = array(); if (empty($QuicktimeIODSvideoProfileNameLookup)) { $QuicktimeIODSvideoProfileNameLookup = array( - 0x00 => 'Reserved (0x00) Profile',
- 0x01 => 'Simple Profile @ Level 1',
- 0x02 => 'Simple Profile @ Level 2',
- 0x03 => 'Simple Profile @ Level 3',
- 0x08 => 'Simple Profile @ Level 0',
- 0x10 => 'Simple Scalable Profile @ Level 0',
- 0x11 => 'Simple Scalable Profile @ Level 1',
- 0x12 => 'Simple Scalable Profile @ Level 2',
- 0x15 => 'AVC/H264 Profile',
- 0x21 => 'Core Profile @ Level 1',
- 0x22 => 'Core Profile @ Level 2',
- 0x32 => 'Main Profile @ Level 2',
- 0x33 => 'Main Profile @ Level 3',
- 0x34 => 'Main Profile @ Level 4',
- 0x42 => 'N-bit Profile @ Level 2',
- 0x51 => 'Scalable Texture Profile @ Level 1',
- 0x61 => 'Simple Face Animation Profile @ Level 1',
- 0x62 => 'Simple Face Animation Profile @ Level 2',
- 0x63 => 'Simple FBA Profile @ Level 1',
- 0x64 => 'Simple FBA Profile @ Level 2',
- 0x71 => 'Basic Animated Texture Profile @ Level 1',
- 0x72 => 'Basic Animated Texture Profile @ Level 2',
- 0x81 => 'Hybrid Profile @ Level 1',
- 0x82 => 'Hybrid Profile @ Level 2',
- 0x91 => 'Advanced Real Time Simple Profile @ Level 1',
- 0x92 => 'Advanced Real Time Simple Profile @ Level 2',
- 0x93 => 'Advanced Real Time Simple Profile @ Level 3',
- 0x94 => 'Advanced Real Time Simple Profile @ Level 4',
- 0xA1 => 'Core Scalable Profile @ Level1',
- 0xA2 => 'Core Scalable Profile @ Level2',
- 0xA3 => 'Core Scalable Profile @ Level3',
- 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
- 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
- 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
- 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
- 0xC1 => 'Advanced Core Profile @ Level 1',
- 0xC2 => 'Advanced Core Profile @ Level 2',
- 0xD1 => 'Advanced Scalable Texture @ Level1',
- 0xD2 => 'Advanced Scalable Texture @ Level2',
- 0xE1 => 'Simple Studio Profile @ Level 1',
- 0xE2 => 'Simple Studio Profile @ Level 2',
- 0xE3 => 'Simple Studio Profile @ Level 3',
- 0xE4 => 'Simple Studio Profile @ Level 4',
- 0xE5 => 'Core Studio Profile @ Level 1',
- 0xE6 => 'Core Studio Profile @ Level 2',
- 0xE7 => 'Core Studio Profile @ Level 3',
- 0xE8 => 'Core Studio Profile @ Level 4',
- 0xF0 => 'Advanced Simple Profile @ Level 0',
- 0xF1 => 'Advanced Simple Profile @ Level 1',
- 0xF2 => 'Advanced Simple Profile @ Level 2',
- 0xF3 => 'Advanced Simple Profile @ Level 3',
- 0xF4 => 'Advanced Simple Profile @ Level 4',
- 0xF5 => 'Advanced Simple Profile @ Level 5',
- 0xF7 => 'Advanced Simple Profile @ Level 3b',
- 0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
- 0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
- 0xFA => 'Fine Granularity Scalable Profile @ Level 2',
- 0xFB => 'Fine Granularity Scalable Profile @ Level 3',
- 0xFC => 'Fine Granularity Scalable Profile @ Level 4',
- 0xFD => 'Fine Granularity Scalable Profile @ Level 5',
- 0xFE => 'Not part of MPEG-4 Visual profiles',
- 0xFF => 'No visual capability required',
+ 0x00 => 'Reserved (0x00) Profile', + 0x01 => 'Simple Profile @ Level 1', + 0x02 => 'Simple Profile @ Level 2', + 0x03 => 'Simple Profile @ Level 3', + 0x08 => 'Simple Profile @ Level 0', + 0x10 => 'Simple Scalable Profile @ Level 0', + 0x11 => 'Simple Scalable Profile @ Level 1', + 0x12 => 'Simple Scalable Profile @ Level 2', + 0x15 => 'AVC/H264 Profile', + 0x21 => 'Core Profile @ Level 1', + 0x22 => 'Core Profile @ Level 2', + 0x32 => 'Main Profile @ Level 2', + 0x33 => 'Main Profile @ Level 3', + 0x34 => 'Main Profile @ Level 4', + 0x42 => 'N-bit Profile @ Level 2', + 0x51 => 'Scalable Texture Profile @ Level 1', + 0x61 => 'Simple Face Animation Profile @ Level 1', + 0x62 => 'Simple Face Animation Profile @ Level 2', + 0x63 => 'Simple FBA Profile @ Level 1', + 0x64 => 'Simple FBA Profile @ Level 2', + 0x71 => 'Basic Animated Texture Profile @ Level 1', + 0x72 => 'Basic Animated Texture Profile @ Level 2', + 0x81 => 'Hybrid Profile @ Level 1', + 0x82 => 'Hybrid Profile @ Level 2', + 0x91 => 'Advanced Real Time Simple Profile @ Level 1', + 0x92 => 'Advanced Real Time Simple Profile @ Level 2', + 0x93 => 'Advanced Real Time Simple Profile @ Level 3', + 0x94 => 'Advanced Real Time Simple Profile @ Level 4', + 0xA1 => 'Core Scalable Profile @ Level1', + 0xA2 => 'Core Scalable Profile @ Level2', + 0xA3 => 'Core Scalable Profile @ Level3', + 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1', + 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2', + 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3', + 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4', + 0xC1 => 'Advanced Core Profile @ Level 1', + 0xC2 => 'Advanced Core Profile @ Level 2', + 0xD1 => 'Advanced Scalable Texture @ Level1', + 0xD2 => 'Advanced Scalable Texture @ Level2', + 0xE1 => 'Simple Studio Profile @ Level 1', + 0xE2 => 'Simple Studio Profile @ Level 2', + 0xE3 => 'Simple Studio Profile @ Level 3', + 0xE4 => 'Simple Studio Profile @ Level 4', + 0xE5 => 'Core Studio Profile @ Level 1', + 0xE6 => 'Core Studio Profile @ Level 2', + 0xE7 => 'Core Studio Profile @ Level 3', + 0xE8 => 'Core Studio Profile @ Level 4', + 0xF0 => 'Advanced Simple Profile @ Level 0', + 0xF1 => 'Advanced Simple Profile @ Level 1', + 0xF2 => 'Advanced Simple Profile @ Level 2', + 0xF3 => 'Advanced Simple Profile @ Level 3', + 0xF4 => 'Advanced Simple Profile @ Level 4', + 0xF5 => 'Advanced Simple Profile @ Level 5', + 0xF7 => 'Advanced Simple Profile @ Level 3b', + 0xF8 => 'Fine Granularity Scalable Profile @ Level 0', + 0xF9 => 'Fine Granularity Scalable Profile @ Level 1', + 0xFA => 'Fine Granularity Scalable Profile @ Level 2', + 0xFB => 'Fine Granularity Scalable Profile @ Level 3', + 0xFC => 'Fine Granularity Scalable Profile @ Level 4', + 0xFD => 'Fine Granularity Scalable Profile @ Level 5', + 0xFE => 'Not part of MPEG-4 Visual profiles', + 0xFF => 'No visual capability required', ); } return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile'); @@ -1706,10 +1818,189 @@ class getid3_quicktime $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom'; $QuicktimeStoreFrontCodeLookup[143441] = 'United States'; } - return (isset($QuicktimeStoreCountryCodeLookup[$sfid]) ? $QuicktimeStoreCountryCodeLookup[$sfid] : 'invalid'); + return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid'); } - function CopyToAppropriateCommentsSection($keyname, $data, &$ThisFileInfo, $boxname='') { + 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 + // Data is stored as records of: + // * 4 bytes record type + // * 2 bytes size of data field type: + // 0x0001 = flag (size field *= 1-byte) + // 0x0002 = char (size field *= 1-byte) + // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB + // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD + // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together + // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? + // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? + // * 2 bytes data size field + // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15") + // all integers are stored BigEndian + + $NCTGtagName = array( + 0x00000001 => 'Make', + 0x00000002 => 'Model', + 0x00000003 => 'Software', + 0x00000011 => 'CreateDate', + 0x00000012 => 'DateTimeOriginal', + 0x00000013 => 'FrameCount', + 0x00000016 => 'FrameRate', + 0x00000022 => 'FrameWidth', + 0x00000023 => 'FrameHeight', + 0x00000032 => 'AudioChannels', + 0x00000033 => 'AudioBitsPerSample', + 0x00000034 => 'AudioSampleRate', + 0x02000001 => 'MakerNoteVersion', + 0x02000005 => 'WhiteBalance', + 0x0200000b => 'WhiteBalanceFineTune', + 0x0200001e => 'ColorSpace', + 0x02000023 => 'PictureControlData', + 0x02000024 => 'WorldTime', + 0x02000032 => 'UnknownInfo', + 0x02000083 => 'LensType', + 0x02000084 => 'Lens', + ); + + $offset = 0; + $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; + switch ($data_size_type) { + case 0x0001: // 0x0001 = flag (size field *= 1-byte) + $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1)); + $offset += ($data_size * 1); + break; + case 0x0002: // 0x0002 = char (size field *= 1-byte) + $data = substr($atom_data, $offset, $data_size * 1); + $offset += ($data_size * 1); + $data = rtrim($data, "\x00"); + break; + case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB + $data = ''; + for ($i = $data_size - 1; $i >= 0; $i--) { + $data .= substr($atom_data, $offset + ($i * 2), 2); + } + $data = getid3_lib::BigEndian2Int($data); + $offset += ($data_size * 2); + break; + case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD + $data = ''; + for ($i = $data_size - 1; $i >= 0; $i--) { + $data .= substr($atom_data, $offset + ($i * 4), 4); + } + $data = getid3_lib::BigEndian2Int($data); + $offset += ($data_size * 4); + break; + case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together + $data = array(); + for ($i = 0; $i < $data_size; $i++) { + $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4)); + $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4)); + if ($denomninator == 0) { + $data[$i] = false; + } else { + $data[$i] = (double) $numerator / $denomninator; + } + } + $offset += (8 * $data_size); + if (count($data) == 1) { + $data = $data[0]; + } + break; + case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? + $data = substr($atom_data, $offset, $data_size * 1); + $offset += ($data_size * 1); + break; + case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? + $data = substr($atom_data, $offset, $data_size * 2); + $offset += ($data_size * 2); + break; + default: +echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>'; + break 2; + } + + switch ($record_type) { + case 0x00000011: // CreateDate + case 0x00000012: // DateTimeOriginal + $data = strtotime($data); + break; + case 0x0200001e: // ColorSpace + switch ($data) { + case 1: + $data = 'sRGB'; + break; + case 2: + $data = 'Adobe RGB'; + break; + } + break; + case 0x02000023: // PictureControlData + $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full'); + $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a'); + $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a'); + $data = array( + 'PictureControlVersion' => substr($data, 0, 4), + 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"), + 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"), + //'?' => substr($data, 44, 4), + 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], + 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), + 'Sharpness' => ord(substr($data, 50, 1)), + 'Contrast' => ord(substr($data, 51, 1)), + 'Brightness' => ord(substr($data, 52, 1)), + 'Saturation' => ord(substr($data, 53, 1)), + 'HueAdjustment' => ord(substr($data, 54, 1)), + 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], + 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], + 'ToningSaturation' => ord(substr($data, 57, 1)), + ); + break; + case 0x02000024: // WorldTime + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime + // timezone is stored as offset from GMT in minutes + $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2)); + if ($timezone & 0x8000) { + $timezone = 0 - (0x10000 - $timezone); + } + $timezone /= 60; + + $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1)); + switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) { + case 2: + $datedisplayformat = 'D/M/Y'; break; + case 1: + $datedisplayformat = 'M/D/Y'; break; + case 0: + default: + $datedisplayformat = 'Y/M/D'; break; + } + + $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat); + break; + case 0x02000083: // LensType + $data = array( + //'_' => $data, + 'mf' => (bool) ($data & 0x01), + 'd' => (bool) ($data & 0x02), + 'g' => (bool) ($data & 0x04), + 'vr' => (bool) ($data & 0x08), + ); + break; + } + $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT)); + $parsed[$tag_name] = $data; + } + return $parsed; + } + + + function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') { static $handyatomtranslatorarray = array(); if (empty($handyatomtranslatorarray)) { $handyatomtranslatorarray['©cpy'] = 'copyright'; @@ -1761,7 +2052,7 @@ class getid3_quicktime $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0 $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0? $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0 - $handyatomtranslatorarray['covr'] = 'artwork'; // iTunes 4.0 + $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0 $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0 $handyatomtranslatorarray['©grp'] = 'grouping'; // iTunes 4.2 $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9 @@ -1797,12 +2088,31 @@ class getid3_quicktime $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id'; $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id'; } + $info = &$this->getid3->info; + $comment_key = ''; if ($boxname && ($boxname != $keyname) && isset($handyatomtranslatorarray[$boxname])) { - $ThisFileInfo['quicktime']['comments'][$handyatomtranslatorarray[$boxname]][] = $data; + $comment_key = $handyatomtranslatorarray[$boxname]; } elseif (isset($handyatomtranslatorarray[$keyname])) { - $ThisFileInfo['quicktime']['comments'][$handyatomtranslatorarray[$keyname]][] = $data; + $comment_key = $handyatomtranslatorarray[$keyname]; + } + if ($comment_key) { + if ($comment_key == 'picture') { + if (!is_array($data)) { + $image_mime = ''; + if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) { + $image_mime = 'image/png'; + } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) { + $image_mime = 'image/jpeg'; + } elseif (preg_match('#^GIF#', $data)) { + $image_mime = 'image/gif'; + } elseif (preg_match('#^BM#', $data)) { + $image_mime = 'image/bmp'; + } + $data = array('data'=>$data, 'image_mime'=>$image_mime); + } + } + $info['quicktime']['comments'][$comment_key][] = $data; } - return true; } diff --git a/modules/getid3/module.audio-video.real.php b/modules/getid3/module.audio-video.real.php index b2c90e74..d716e7da 100644 --- a/modules/getid3/module.audio-video.real.php +++ b/modules/getid3/module.audio-video.real.php @@ -15,67 +15,69 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -class getid3_real +class getid3_real extends getid3_handler { - function getid3_real(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'real'; - $ThisFileInfo['bitrate'] = 0; - $ThisFileInfo['playtime_seconds'] = 0; + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $info['fileformat'] = 'real'; + $info['bitrate'] = 0; + $info['playtime_seconds'] = 0; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $ChunkCounter = 0; - while (ftell($fd) < $ThisFileInfo['avdataend']) { - $ChunkData = fread($fd, 8); + while (ftell($this->getid3->fp) < $info['avdataend']) { + $ChunkData = fread($this->getid3->fp, 8); $ChunkName = substr($ChunkData, 0, 4); $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4)); if ($ChunkName == '.ra'."\xFD") { - $ChunkData .= fread($fd, $ChunkSize - 8); - if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) { - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['real']['old_ra_header']['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['real']['old_ra_header']['channels']; - - $ThisFileInfo['playtime_seconds'] = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']); - $ThisFileInfo['audio']['bitrate'] = 8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']); - $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']); - - foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) { + $ChunkData .= fread($this->getid3->fp, $ChunkSize - 8); + if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) { + $info['audio']['dataformat'] = 'real'; + $info['audio']['lossless'] = false; + $info['audio']['sample_rate'] = $info['real']['old_ra_header']['sample_rate']; + $info['audio']['bits_per_sample'] = $info['real']['old_ra_header']['bits_per_sample']; + $info['audio']['channels'] = $info['real']['old_ra_header']['channels']; + + $info['playtime_seconds'] = 60 * ($info['real']['old_ra_header']['audio_bytes'] / $info['real']['old_ra_header']['bytes_per_minute']); + $info['audio']['bitrate'] = 8 * ($info['real']['old_ra_header']['audio_bytes'] / $info['playtime_seconds']); + $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($info['real']['old_ra_header']['fourcc'], $info['audio']['bitrate']); + + foreach ($info['real']['old_ra_header']['comments'] as $key => $valuearray) { if (strlen(trim($valuearray[0])) > 0) { - $ThisFileInfo['real']['comments'][$key][] = trim($valuearray[0]); + $info['real']['comments'][$key][] = trim($valuearray[0]); } } return true; } - $ThisFileInfo['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org'; - unset($ThisFileInfo['bitrate']); - unset($ThisFileInfo['playtime_seconds']); + $info['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org'; + unset($info['bitrate']); + unset($info['playtime_seconds']); return false; } // shortcut - $ThisFileInfo['real']['chunks'][$ChunkCounter] = array(); - $thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter]; + $info['real']['chunks'][$ChunkCounter] = array(); + $thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter]; $thisfile_real_chunks_currentchunk['name'] = $ChunkName; - $thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 8; + $thisfile_real_chunks_currentchunk['offset'] = ftell($this->getid3->fp) - 8; $thisfile_real_chunks_currentchunk['length'] = $ChunkSize; - if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file'; + if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) { + $info['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file'; return false; } - if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) { + if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) { - $ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8); - fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET); + $ChunkData .= fread($this->getid3->fp, $this->getid3->fread_buffer_size() - 8); + fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET); } elseif(($ChunkSize - 8) > 0) { - $ChunkData .= fread($fd, $ChunkSize - 8); + $ChunkData .= fread($this->getid3->fp, $ChunkSize - 8); } $offset = 8; @@ -95,7 +97,7 @@ class getid3_real break; default: - //$ThisFileInfo['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)'; + //$info['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)'; break; } @@ -128,9 +130,9 @@ class getid3_real $offset += 2; $thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; - $ThisFileInfo['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000; + $info['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000; if ($thisfile_real_chunks_currentchunk['duration'] > 0) { - $ThisFileInfo['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $info['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate']; } $thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001); $thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002); @@ -202,24 +204,24 @@ class getid3_real $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']); - $ThisFileInfo['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height']; - $ThisFileInfo['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second']; - $ThisFileInfo['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec']; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample']; + $info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width']; + $info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height']; + $info['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second']; + $info['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec']; + $info['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample']; break; case 'audio/x-pn-realaudio': case 'audio/x-pn-multirate-realaudio': $this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']); - $ThisFileInfo['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample']; - $ThisFileInfo['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels']; - if (!empty($ThisFileInfo['audio']['dataformat'])) { - foreach ($ThisFileInfo['audio'] as $key => $value) { + $info['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate']; + $info['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample']; + $info['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels']; + if (!empty($info['audio']['dataformat'])) { + foreach ($info['audio'] as $key => $value) { if ($key != 'streams') { - $ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value; + $info['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value; } } } @@ -255,36 +257,36 @@ class getid3_real } - if (empty($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000); + if (empty($info['playtime_seconds'])) { + $info['playtime_seconds'] = max($info['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000); } if ($thisfile_real_chunks_currentchunk['duration'] > 0) { switch ($thisfile_real_chunks_currentchunk['mime_type']) { case 'audio/x-pn-realaudio': case 'audio/x-pn-multirate-realaudio': - $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $ThisFileInfo['audio']['bitrate']); - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = false; + $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $info['audio']['bitrate']); + $info['audio']['dataformat'] = 'real'; + $info['audio']['lossless'] = false; break; case 'video/x-pn-realvideo': case 'video/x-pn-multirate-realvideo': - $ThisFileInfo['video']['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['video']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['video']['dataformat'] = 'real'; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $info['video']['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $info['video']['bitrate_mode'] = 'cbr'; + $info['video']['dataformat'] = 'real'; + $info['video']['lossless'] = false; + $info['video']['pixel_aspect_ratio'] = (float) 1; break; case 'audio/x-ralf-mpeg4-generic': - $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['audio']['codec'] = 'RealAudio Lossless'; - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = true; + $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $info['audio']['codec'] = 'RealAudio Lossless'; + $info['audio']['dataformat'] = 'real'; + $info['audio']['lossless'] = true; break; } - $ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0); + $info['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0); } } break; @@ -317,7 +319,7 @@ class getid3_real $commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment'); foreach ($commentkeystocopy as $key => $val) { if ($thisfile_real_chunks_currentchunk[$key]) { - $ThisFileInfo['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]); + $info['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]); } } @@ -345,22 +347,22 @@ class getid3_real break 2; } else { // non-last index chunk, seek to next index chunk (skipping actual index data) - fseek($fd, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET); + fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET); } } break; default: - $ThisFileInfo['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']; + $info['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']; break; } $ChunkCounter++; } - if (!empty($ThisFileInfo['audio']['streams'])) { - $ThisFileInfo['audio']['bitrate'] = 0; - foreach ($ThisFileInfo['audio']['streams'] as $key => $valuearray) { - $ThisFileInfo['audio']['bitrate'] += $valuearray['bitrate']; + if (!empty($info['audio']['streams'])) { + $info['audio']['bitrate'] = 0; + foreach ($info['audio']['streams'] as $key => $valuearray) { + $info['audio']['bitrate'] += $valuearray['bitrate']; } } diff --git a/modules/getid3/module.audio-video.riff.php b/modules/getid3/module.audio-video.riff.php index 64b160e1..cc7d05eb 100644 --- a/modules/getid3/module.audio-video.riff.php +++ b/modules/getid3/module.audio-video.riff.php @@ -19,73 +19,86 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); -class getid3_riff +class getid3_riff extends getid3_handler { - function getid3_riff(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // initialize these values to an empty array, otherwise they default to NULL // and you can't append array values to a NULL value - $ThisFileInfo['riff'] = array('raw'=>array()); + $info['riff'] = array('raw'=>array()); // Shortcuts - $thisfile_riff = &$ThisFileInfo['riff']; + $thisfile_riff = &$info['riff']; $thisfile_riff_raw = &$thisfile_riff['raw']; - $thisfile_audio = &$ThisFileInfo['audio']; - $thisfile_video = &$ThisFileInfo['video']; + $thisfile_audio = &$info['audio']; + $thisfile_video = &$info['video']; $thisfile_audio_dataformat = &$thisfile_audio['dataformat']; $thisfile_riff_audio = &$thisfile_riff['audio']; $thisfile_riff_video = &$thisfile_riff['video']; - $Original['avdataoffset'] = $ThisFileInfo['avdataoffset']; - $Original['avdataend'] = $ThisFileInfo['avdataend']; + $Original['avdataoffset'] = $info['avdataoffset']; + $Original['avdataend'] = $info['avdataend']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $RIFFheader = fread($fd, 12); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $RIFFheader = fread($this->getid3->fp, 12); $RIFFsubtype = substr($RIFFheader, 8, 4); switch (substr($RIFFheader, 0, 4)) { case 'FORM': - $ThisFileInfo['fileformat'] = 'aiff'; - $thisfile_riff['header_size'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); - $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $ThisFileInfo['avdataoffset'] + 12, $ThisFileInfo['avdataoffset'] + $thisfile_riff['header_size'], $ThisFileInfo); + $info['fileformat'] = 'aiff'; + $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4)); + $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']); break; case 'RIFF': // AVI, WAV, etc case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s - $ThisFileInfo['fileformat'] = 'riff'; - $thisfile_riff['header_size'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); + $info['fileformat'] = 'riff'; + $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4)); if ($RIFFsubtype == 'RMP3') { // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s $RIFFsubtype = 'WAVE'; } - $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $ThisFileInfo['avdataoffset'] + 12, $ThisFileInfo['avdataoffset'] + $thisfile_riff['header_size'], $ThisFileInfo); + $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']); + if (($info['avdataend'] - $info['filesize']) == 1) { + // LiteWave appears to incorrectly *not* pad actual output file + // to nearest WORD boundary so may appear to be short by one + // byte, in which case - skip warning + $info['avdataend'] = $info['filesize']; + } $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset - while ($nextRIFFoffset < $ThisFileInfo['filesize']) { + while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) { if (!getid3_lib::intValueSupported($nextRIFFoffset + 1024)) { - $ThisFileInfo['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong'; - $ThisFileInfo['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present'; + $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong'; + $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present'; break; } else { - fseek($fd, $nextRIFFoffset, SEEK_SET); - $nextRIFFheader = fread($fd, 12); - $nextRIFFheaderID = substr($nextRIFFheader, 0, 4); - $nextRIFFsize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($nextRIFFheader, 4, 4)); - $nextRIFFtype = substr($nextRIFFheader, 8, 4); + fseek($this->getid3->fp, $nextRIFFoffset, SEEK_SET); + $nextRIFFheader = fread($this->getid3->fp, 12); + if ($nextRIFFoffset == ($info['avdataend'] - 1)) { + if (substr($nextRIFFheader, 0, 1) == "\x00") { + // RIFF padded to WORD boundary, we're actually already at the end + break; + } + } + $nextRIFFheaderID = substr($nextRIFFheader, 0, 4); + $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4)); + $nextRIFFtype = substr($nextRIFFheader, 8, 4); $chunkdata = array(); $chunkdata['offset'] = $nextRIFFoffset + 8; $chunkdata['size'] = $nextRIFFsize; $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size']; switch ($nextRIFFheaderID) { case 'RIFF': - $ThisFileInfo['avdataend'] = $nextRIFFoffset; - if (!getid3_lib::intValueSupported($ThisFileInfo['avdataend'])) { - $ThisFileInfo['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong'; - $ThisFileInfo['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present'; + $info['avdataend'] = $nextRIFFoffset; + if (!getid3_lib::intValueSupported($info['avdataend'])) { + $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong'; + $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present'; } - $chunkdata['chunks'] = getid3_riff::ParseRIFF($fd, $chunkdata['offset'] + 4, $chunkdata['offset'] + $chunkdata['size'], $ThisFileInfo); + $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $chunkdata['offset'] + $chunkdata['size']); if (!isset($thisfile_riff[$nextRIFFtype])) { $thisfile_riff[$nextRIFFtype] = array(); @@ -97,10 +110,11 @@ class getid3_riff $thisfile_riff[$nextRIFFheaderID][] = $chunkdata; break; default: - if ($ThisFileInfo['filesize'] == ($chunkdata['offset'] - 8 + 128)) { - $DIVXTAG = $nextRIFFheader.fread($fd, 128 - 12); + if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) { + $DIVXTAG = $nextRIFFheader.fread($this->getid3->fp, 128 - 12); if (substr($DIVXTAG, -7) == 'DIVXTAG') { // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file + $info['warning'][] = 'Found wrongly-structured DIVXTAG at offset '.(ftell($this->getid3->fp) - 128 + 12).', parsing anyway'; $thisfile_riff['DIVXTAG'] = $this->ParseDIVXTAG($DIVXTAG); foreach ($thisfile_riff['DIVXTAG'] as $key => $value) { if ($value && !preg_match('#_id$#', $key)) { @@ -110,7 +124,7 @@ class getid3_riff break 2; } } - $ThisFileInfo['warning'][] = 'expecting "RIFF" or "JUNK" at '.$nextRIFFoffset.', found '.substr($nextRIFFheader, 0, 4).' - skipping rest of file'; + $info['warning'][] = 'expecting "RIFF" or "JUNK" at '.$nextRIFFoffset.', found '.getid3_lib::PrintHexBytes(substr($nextRIFFheader, 0, 4)).' - skipping rest of file'; break 2; } } @@ -121,8 +135,8 @@ class getid3_riff break; default: - $ThisFileInfo['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'; - unset($ThisFileInfo['fileformat']); + $info['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'; + unset($info['fileformat']); return false; break; } @@ -138,15 +152,15 @@ class getid3_riff } if (isset($thisfile_riff_WAVE['data'][0]['offset'])) { - $ThisFileInfo['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8; - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size']; + $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size']; } if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) { $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']); $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) { - $ThisFileInfo['error'][] = 'Corrupt RIFF file: bitrate_audio == zero'; + $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero'; return false; } $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw']; @@ -155,11 +169,11 @@ class getid3_riff $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { - $ThisFileInfo['warning'][] = 'Audio codec = '.$thisfile_audio['codec']; + $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec']; } $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; - $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $thisfile_audio['bitrate']); + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']); $thisfile_audio['lossless'] = false; if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { @@ -194,9 +208,9 @@ class getid3_riff $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track']; $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album']; - $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); - $thisfile_riff_raw_rgad['nRadioRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2)); - $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2)); + $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); + $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2)); + $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2)); $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); @@ -223,14 +237,14 @@ class getid3_riff } if (isset($thisfile_riff_WAVE['fact'][0]['data'])) { - $thisfile_riff_raw['fact']['NumberOfSamples'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4)); + $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4)); // This should be a good way of calculating exact playtime, // but some sample files have had incorrect number of samples, // so cannot use this method // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) { - // $ThisFileInfo['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; + // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; // } } if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) { @@ -248,7 +262,7 @@ class getid3_riff $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8); $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1)); - $thisfile_riff_WAVE_bext_0['reserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 347, 254)); + $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254); $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601))); if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) { if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) { @@ -256,10 +270,10 @@ class getid3_riff list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time; $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']); } else { - $ThisFileInfo['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid'; + $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid'; } } else { - $ThisFileInfo['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid'; + $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid'; } $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author']; $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title']; @@ -339,10 +353,10 @@ class getid3_riff $SNDM_thisTagOffset += $SNDM_thisTagDataSize; if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) { - $ThisFileInfo['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; break; } elseif ($SNDM_thisTagSize <= 0) { - $ThisFileInfo['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; break; } $SNDM_startoffset += $SNDM_thisTagSize; @@ -351,7 +365,7 @@ class getid3_riff if ($parsedkey = $this->RIFFwaveSNDMtagLookup($SNDM_thisTagKey)) { $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText; } else { - $ThisFileInfo['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; } } @@ -370,38 +384,50 @@ class getid3_riff if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) { $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; - $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $thisfile_audio['bitrate']); + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']); } - if (!empty($ThisFileInfo['wavpack'])) { + if (!empty($info['wavpack'])) { $thisfile_audio_dataformat = 'wavpack'; $thisfile_audio['bitrate_mode'] = 'vbr'; - $thisfile_audio['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['version']; + $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version']; // Reset to the way it was - RIFF parsing will have messed this up - $ThisFileInfo['avdataend'] = $Original['avdataend']; - $thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['avdataend'] = $Original['avdataend']; + $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; - fseek($fd, $ThisFileInfo['avdataoffset'] - 44, SEEK_SET); - $RIFFdata = fread($fd, 44); + fseek($this->getid3->fp, $info['avdataoffset'] - 44, SEEK_SET); + $RIFFdata = fread($this->getid3->fp, 44); $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { - $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); - fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); - $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); + $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); + $RIFFdata .= fread($this->getid3->fp, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); } // move the data chunk after all other chunks (if any) // so that the RIFF parser doesn't see EOF when trying // to skip over the data chunk $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); + $getid3_riff = new getid3_riff($this->getid3); + $getid3_riff->ParseRIFFdata($RIFFdata); + unset($getid3_riff); } if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { + case 0x0001: // PCM + if (!empty($info['ac3'])) { + // Dolby Digital WAV files masquerade as PCM-WAV, but they're not + $thisfile_audio['wformattag'] = 0x2000; + $thisfile_audio['codec'] = $this->RIFFwFormatTagLookup($thisfile_audio['wformattag']); + $thisfile_audio['lossless'] = false; + $thisfile_audio['bitrate'] = $info['ac3']['bitrate']; + $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate']; + } + break; case 0x08AE: // ClearJump LiteWave $thisfile_audio['bitrate_mode'] = 'vbr'; $thisfile_audio_dataformat = 'litewave'; @@ -453,7 +479,7 @@ class getid3_riff break; } } - if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { + if ($info['avdataend'] > $info['filesize']) { switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') { case 'wavpack': // WavPack case 'lpac': // LPAC @@ -463,41 +489,41 @@ class getid3_riff break; case 'litewave': - if (($ThisFileInfo['avdataend'] - $ThisFileInfo['filesize']) == 1) { + if (($info['avdataend'] - $info['filesize']) == 1) { // LiteWave appears to incorrectly *not* pad actual output file // to nearest WORD boundary so may appear to be short by one // byte, in which case - skip warning } else { // Short by more than one byte, throw warning - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset'])).' bytes)'; - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; + $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; } break; default: - if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']) % 2) == 1)) { + if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) { // output file appears to be incorrectly *not* padded to nearest WORD boundary // Output less severe warning - $ThisFileInfo['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset'])).' bytes)'; - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; + $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; } else { // Short by more than one byte, throw warning - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset'])).' bytes)'; - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; + $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; } break; } } - if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'])) { - if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes']) == 1) { - $ThisFileInfo['avdataend']--; - $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) { + if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) { + $info['avdataend']--; + $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; } } if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) { unset($thisfile_audio['bits_per_sample']); - if (!empty($ThisFileInfo['ac3']['bitrate']) && ($ThisFileInfo['ac3']['bitrate'] != $thisfile_audio['bitrate'])) { - $thisfile_audio['bitrate'] = $ThisFileInfo['ac3']['bitrate']; + if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) { + $thisfile_audio['bitrate'] = $info['ac3']['bitrate']; } } break; @@ -505,18 +531,18 @@ class getid3_riff case 'AVI ': $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably $thisfile_video['dataformat'] = 'avi'; - $ThisFileInfo['mime_type'] = 'video/avi'; + $info['mime_type'] = 'video/avi'; if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { - $ThisFileInfo['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; if (isset($thisfile_riff['AVIX'])) { - $ThisFileInfo['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size']; + $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size']; } else { - $ThisFileInfo['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size']; + $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size']; } - if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes of data, only found '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' (short by '.($ThisFileInfo['avdataend'] - $ThisFileInfo['filesize']).' bytes)'; - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; + if ($info['avdataend'] > $info['filesize']) { + $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)'; + $info['avdataend'] = $info['filesize']; } } @@ -534,12 +560,12 @@ class getid3_riff foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) { $thisfile_riff_avi_hdrl_strl_indx_stream_data = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data']; - $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 0, 2)); - $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 2, 1)); - $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 3, 1)); - $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 4, 4)); - $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 8, 4); - $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 12, 4)); + $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 0, 2)); + $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 2, 1)); + $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 3, 1)); + $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 4, 4)); + $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 8, 4); + $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 12, 4)); //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']]; //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']]; @@ -554,24 +580,24 @@ class getid3_riff $thisfile_riff_raw['avih'] = array(); $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih']; - $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 0, 4)); // frame display rate (or 0L) + $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L) if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; + $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; return false; } - $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 4, 4)); // max. transfer rate - $thisfile_riff_raw_avih['dwPaddingGranularity'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. - $thisfile_riff_raw_avih['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags - $thisfile_riff_raw_avih['dwTotalFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file - $thisfile_riff_raw_avih['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4)); - $thisfile_riff_raw_avih['dwStreams'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4)); - $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4)); - $thisfile_riff_raw_avih['dwWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4)); - $thisfile_riff_raw_avih['dwHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4)); - $thisfile_riff_raw_avih['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4)); - $thisfile_riff_raw_avih['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4)); - $thisfile_riff_raw_avih['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4)); - $thisfile_riff_raw_avih['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 52, 4)); + $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = $this->EitherEndian2Int(substr($avihData, 4, 4)); // max. transfer rate + $thisfile_riff_raw_avih['dwPaddingGranularity'] = $this->EitherEndian2Int(substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. + $thisfile_riff_raw_avih['dwFlags'] = $this->EitherEndian2Int(substr($avihData, 12, 4)); // the ever-present flags + $thisfile_riff_raw_avih['dwTotalFrames'] = $this->EitherEndian2Int(substr($avihData, 16, 4)); // # frames in file + $thisfile_riff_raw_avih['dwInitialFrames'] = $this->EitherEndian2Int(substr($avihData, 20, 4)); + $thisfile_riff_raw_avih['dwStreams'] = $this->EitherEndian2Int(substr($avihData, 24, 4)); + $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($avihData, 28, 4)); + $thisfile_riff_raw_avih['dwWidth'] = $this->EitherEndian2Int(substr($avihData, 32, 4)); + $thisfile_riff_raw_avih['dwHeight'] = $this->EitherEndian2Int(substr($avihData, 36, 4)); + $thisfile_riff_raw_avih['dwScale'] = $this->EitherEndian2Int(substr($avihData, 40, 4)); + $thisfile_riff_raw_avih['dwRate'] = $this->EitherEndian2Int(substr($avihData, 44, 4)); + $thisfile_riff_raw_avih['dwStart'] = $this->EitherEndian2Int(substr($avihData, 48, 4)); + $thisfile_riff_raw_avih['dwLength'] = $this->EitherEndian2Int(substr($avihData, 52, 4)); $thisfile_riff_raw_avih['flags']['hasindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000010); $thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000020); @@ -689,20 +715,20 @@ class getid3_riff $thisfile_riff_raw['strh'][$i] = array(); $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i]; - $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; - $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4); - $thisfile_riff_raw_strh_current['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 8, 4)); // Contains AVITF_* flags - $thisfile_riff_raw_strh_current['wPriority'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2)); - $thisfile_riff_raw_strh_current['wLanguage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2)); - $thisfile_riff_raw_strh_current['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4)); - $thisfile_riff_raw_strh_current['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4)); - $thisfile_riff_raw_strh_current['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4)); - $thisfile_riff_raw_strh_current['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4)); - $thisfile_riff_raw_strh_current['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4)); - $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4)); - $thisfile_riff_raw_strh_current['dwQuality'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4)); - $thisfile_riff_raw_strh_current['dwSampleSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4)); - $thisfile_riff_raw_strh_current['rcFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4)); + $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; + $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4); + $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags + $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2)); + $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2)); + $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4)); + $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4)); + $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4)); + $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4)); + $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4)); + $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4)); + $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4)); + $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4)); + $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4)); $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']); $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler']; @@ -726,19 +752,8 @@ class getid3_riff switch ($strhfccType) { case 'vids': - $thisfile_riff_raw_strf_strhfccType_streamindex = getid3_riff::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($ThisFileInfo['fileformat'] == 'riff')); - //$thisfile_riff_raw_strf_strhfccType_streamindex['biSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure - //$thisfile_riff_raw_strf_strhfccType_streamindex['biWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 4, 4)); // width of the bitmap in pixels - //$thisfile_riff_raw_strf_strhfccType_streamindex['biHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner - //$thisfile_riff_raw_strf_strhfccType_streamindex['biPlanes'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 - //$thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels - //$thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'] = substr($strfData, 16, 4); // - //$thisfile_riff_raw_strf_strhfccType_streamindex['biSizeImage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) - //$thisfile_riff_raw_strf_strhfccType_streamindex['biXPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device - //$thisfile_riff_raw_strf_strhfccType_streamindex['biYPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device - //$thisfile_riff_raw_strf_strhfccType_streamindex['biClrUsed'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression - //$thisfile_riff_raw_strf_strhfccType_streamindex['biClrImportant'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important - + $thisfile_riff_raw_strf_strhfccType_streamindex = getid3_riff::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($info['fileformat'] == 'riff')); +//echo '<pre>'.print_r($thisfile_riff_raw_strf_strhfccType_streamindex, true).'</pre>'; $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']; if ($thisfile_riff_video_current['codec'] == 'DV') { @@ -753,30 +768,32 @@ class getid3_riff break; default: - $ThisFileInfo['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"'; + $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"'; break; } } } - if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { - $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); - $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; - $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + if (getid3_riff::RIFFfourccLookup($thisfile_video['fourcc'])) { + $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_video['fourcc']); + $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; + } switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) { case 'HFYU': // Huffman Lossless Codec case 'IRAW': // Intel YUV Uncompressed case 'YUY2': // Uncompressed YUV 4:2:2 $thisfile_video['lossless'] = true; - $thisfile_video['bits_per_sample'] = 24; + //$thisfile_video['bits_per_sample'] = 24; break; default: $thisfile_video['lossless'] = false; - $thisfile_video['bits_per_sample'] = 24; + //$thisfile_video['bits_per_sample'] = 24; break; } @@ -790,26 +807,26 @@ class getid3_riff $thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio_dataformat = 'cda'; $thisfile_audio['lossless'] = true; - unset($ThisFileInfo['mime_type']); + unset($info['mime_type']); - $ThisFileInfo['avdataoffset'] = 44; + $info['avdataoffset'] = 44; if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) { // shortcut $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0]; - $thisfile_riff_CDDA_fmt_0['unknown1'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); - $thisfile_riff_CDDA_fmt_0['track_num'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); - $thisfile_riff_CDDA_fmt_0['disc_id'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); - $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); - $thisfile_riff_CDDA_fmt_0['playtime_frames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); - $thisfile_riff_CDDA_fmt_0['unknown6'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); - $thisfile_riff_CDDA_fmt_0['unknown7'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4)); + $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); + $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); + $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); + $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); + $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); + $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); + $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4)); $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75; $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75; - $ThisFileInfo['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; + $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; + $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; // hardcoded data for CD-audio $thisfile_audio['sample_rate'] = 44100; @@ -826,19 +843,19 @@ class getid3_riff $thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio_dataformat = 'aiff'; $thisfile_audio['lossless'] = true; - $ThisFileInfo['mime_type'] = 'audio/x-aiff'; + $info['mime_type'] = 'audio/x-aiff'; if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { - $ThisFileInfo['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; - if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { - if (($ThisFileInfo['avdataend'] == ($ThisFileInfo['filesize'] + 1)) && (($ThisFileInfo['filesize'] % 2) == 1)) { + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; + if ($info['avdataend'] > $info['filesize']) { + if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) { // structures rounded to 2-byte boundary, but dumb encoders // forget to pad end of file to make this actually work } else { - $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' bytes found'; + $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found'; } - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; + $info['avdataend'] = $info['filesize']; } } @@ -893,10 +910,10 @@ class getid3_riff } $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate']; if ($thisfile_audio['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupted AIFF file: sample_rate == zero'; + $info['error'][] = 'Corrupted AIFF file: sample_rate == zero'; return false; } - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; + $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; } if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) { @@ -904,17 +921,17 @@ class getid3_riff $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); $offset += 2; for ($i = 0; $i < $CommentCount; $i++) { - $ThisFileInfo['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); + $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); $offset += 4; - $ThisFileInfo['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); + $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); $offset += 2; $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); $offset += 2; - $ThisFileInfo['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); + $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); $offset += $CommentLength; - $ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']); - $thisfile_riff['comments']['comment'][] = $ThisFileInfo['comments_raw'][$i]['comment']; + $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']); + $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment']; } } @@ -931,13 +948,13 @@ class getid3_riff $thisfile_audio_dataformat = '8svx'; $thisfile_audio['bits_per_sample'] = 8; $thisfile_audio['channels'] = 1; // overridden below, if need be - $ThisFileInfo['mime_type'] = 'audio/x-aiff'; + $info['mime_type'] = 'audio/x-aiff'; if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { - $ThisFileInfo['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; - if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']).' bytes found'; + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; + if ($info['avdataend'] > $info['filesize']) { + $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found'; } } @@ -959,17 +976,17 @@ class getid3_riff case 0: $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; $thisfile_audio['lossless'] = true; - $ActualBitsPerSample = 8; + $ActualBitsPerSample = 8; break; case 1: $thisfile_audio['codec'] = 'Fibonacci-delta encoding'; $thisfile_audio['lossless'] = false; - $ActualBitsPerSample = 4; + $ActualBitsPerSample = 4; break; default: - $ThisFileInfo['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'; + $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'; break; } } @@ -987,7 +1004,7 @@ class getid3_riff break; default: - $ThisFileInfo['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"'; + $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"'; break; } @@ -1002,41 +1019,41 @@ class getid3_riff $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels']; if (!empty($thisfile_audio['bitrate'])) { - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($thisfile_audio['bitrate'] / 8); + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8); } break; case 'CDXA': - $ThisFileInfo['mime_type'] = 'video/mpeg'; + $info['mime_type'] = 'video/mpeg'; if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) { - $dummy = $ThisFileInfo; - $dummy['error'] = array(); - $mpeg_scanner = new getid3_mpeg($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['video'] = $dummy['video']; - $ThisFileInfo['mpeg'] = $dummy['mpeg']; - $ThisFileInfo['warning'] = $dummy['warning']; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_mpeg = new getid3_mpeg($getid3_temp); + $getid3_mpeg->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['video'] = $getid3_temp->info['video']; + $info['mpeg'] = $getid3_temp->info['mpeg']; + $info['warning'] = $getid3_temp->info['warning']; } - unset($mpeg_scanner); + unset($getid3_temp, $getid3_mpeg); } } break; default: - $ThisFileInfo['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; - unset($ThisFileInfo['fileformat']); + $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; + unset($info['fileformat']); break; } if (isset($thisfile_riff_raw['fmt ']['wFormatTag']) && ($thisfile_riff_raw['fmt ']['wFormatTag'] == 1)) { // http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $FirstFourBytes = fread($fd, 4); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $FirstFourBytes = fread($this->getid3->fp, 4); if (preg_match('/^\xFF\x1F\x00\xE8/s', $FirstFourBytes)) { // DTSWAV $thisfile_audio_dataformat = 'dts'; @@ -1057,48 +1074,48 @@ class getid3_riff $this->RIFFcommentsParse($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']); } - if (empty($thisfile_audio['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { - $thisfile_audio['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; + if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) { + $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version']; } - if (!isset($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['playtime_seconds'] = 0; + if (!isset($info['playtime_seconds'])) { + $info['playtime_seconds'] = 0; } if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); + $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); + $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); } - if ($ThisFileInfo['playtime_seconds'] > 0) { + if ($info['playtime_seconds'] > 0) { if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { - if (!isset($ThisFileInfo['bitrate'])) { - $ThisFileInfo['bitrate'] = ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8); + if (!isset($info['bitrate'])) { + $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); } } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { if (!isset($thisfile_audio['bitrate'])) { - $thisfile_audio['bitrate'] = ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8); + $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); } } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { if (!isset($thisfile_video['bitrate'])) { - $thisfile_video['bitrate'] = ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8); + $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); } } } - if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($ThisFileInfo['playtime_seconds'] > 0)) { + if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) { - $ThisFileInfo['bitrate'] = ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8); + $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); $thisfile_audio['bitrate'] = 0; - $thisfile_video['bitrate'] = $ThisFileInfo['bitrate']; + $thisfile_video['bitrate'] = $info['bitrate']; foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) { $thisfile_video['bitrate'] -= $audioinfoarray['bitrate']; $thisfile_audio['bitrate'] += $audioinfoarray['bitrate']; @@ -1111,14 +1128,14 @@ class getid3_riff } } - if (isset($ThisFileInfo['mpeg']['audio'])) { - $thisfile_audio_dataformat = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; - $thisfile_audio['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $thisfile_audio['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $thisfile_audio['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $thisfile_audio['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - if (!empty($ThisFileInfo['mpeg']['audio']['codec'])) { - $thisfile_audio['codec'] = $ThisFileInfo['mpeg']['audio']['codec'].' '.$thisfile_audio['codec']; + if (isset($info['mpeg']['audio'])) { + $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer']; + $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $thisfile_audio['channels'] = $info['mpeg']['audio']['channels']; + $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate']; + $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + if (!empty($info['mpeg']['audio']['codec'])) { + $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec']; } if (!empty($thisfile_audio['streams'])) { foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) { @@ -1131,7 +1148,9 @@ class getid3_riff } } } - $thisfile_audio['encoder_options'] = getid3_mp3::GuessEncoderOptions($ThisFileInfo); + $getid3_mp3 = new getid3_mp3($this->getid3); + $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions(); + unset($getid3_mp3); } @@ -1220,35 +1239,37 @@ class getid3_riff return true; } - static function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) { - $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']); + function ParseRIFF($startoffset, $maxoffset) { + $info = &$this->getid3->info; + + $maxoffset = min($maxoffset, $info['avdataend']); $RIFFchunk = false; $FoundAllChunksWeNeed = false; if (($startoffset < 0) || !getid3_lib::intValueSupported($startoffset)) { - $ThisFileInfo['warning'][] = 'Unable to ParseRIFF() at '.$startoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['warning'][] = 'Unable to ParseRIFF() at '.$startoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; return false; } $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset); if ($maxoffset > $max_usable_offset) { - $ThisFileInfo['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at '.$startoffset.' because beyond it extends to '.$maxoffset.', which is beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at '.$startoffset.' because beyond it extends to '.$maxoffset.', which is beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; } - fseek($fd, $startoffset, SEEK_SET); + fseek($this->getid3->fp, $startoffset, SEEK_SET); - while (ftell($fd) < $max_usable_offset) { - $chunknamesize = fread($fd, 8); - $chunkname = substr($chunknamesize, 0, 4); - $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($chunknamesize, 4, 4)); + while (ftell($this->getid3->fp) < $max_usable_offset) { + $chunknamesize = fread($this->getid3->fp, 8); + $chunkname = substr($chunknamesize, 0, 4); + $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4)); if (strlen($chunkname) < 4) { - $ThisFileInfo['error'][] = 'Expecting chunk name at offset '.(ftell($fd) - 4).' but found nothing. Aborting RIFF parsing.'; + $info['error'][] = 'Expecting chunk name at offset '.(ftell($this->getid3->fp) - 4).' but found nothing. Aborting RIFF parsing.'; break; } if ($chunksize == 0) { if ($chunkname == 'JUNK') { // we'll allow zero-size JUNK frames } else { - $ThisFileInfo['warning'][] = 'Chunk size at offset '.(ftell($fd) - 4).' is zero. Aborting RIFF parsing.'; + $info['warning'][] = 'Chunk size at offset '.(ftell($this->getid3->fp) - 4).' is zero. Aborting RIFF parsing.'; break; } } @@ -1259,9 +1280,9 @@ class getid3_riff switch ($chunkname) { case 'LIST': - $listname = fread($fd, 4); + $listname = fread($this->getid3->fp, 4); if (preg_match('#^(movi|rec )$#i', $listname)) { - $RIFFchunk[$listname]['offset'] = ftell($fd) - 4; + $RIFFchunk[$listname]['offset'] = ftell($this->getid3->fp) - 4; $RIFFchunk[$listname]['size'] = $chunksize; if ($FoundAllChunksWeNeed) { @@ -1270,8 +1291,8 @@ class getid3_riff } else { - $WhereWeWere = ftell($fd); - $AudioChunkHeader = fread($fd, 12); + $WhereWeWere = ftell($this->getid3->fp); + $AudioChunkHeader = fread($this->getid3->fp, 12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); @@ -1281,40 +1302,45 @@ class getid3_riff if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = ftell($fd) - 4; - $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; - getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false); - if (isset($dummy['mpeg']['audio'])) { - $ThisFileInfo = $dummy; - $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4; + $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize; + $getid3_mp3 = new getid3_mp3($getid3_temp); + $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); + if (isset($getid3_temp->info['mpeg']['audio'])) { + $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; + $info['audio'] = $getid3_temp->info['audio']; + $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer']; + $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($dummy); + unset($getid3_temp, $getid3_mp3); } } elseif (preg_match('/^\x0B\x77/s', $FirstFourBytes)) { // AC3 - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = ftell($fd) - 4; - $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; - $dummy['error'] = array(); - $ac3_tag = new getid3_ac3($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4; + $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize; + $getid3_ac3 = new getid3_ac3($getid3_temp); + $getid3_ac3->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['ac3'] = $getid3_temp->info['ac3']; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $key => $value) { + $info['warning'][] = $value; + } + } } - unset($ac3_tag); - + unset($getid3_temp, $getid3_ac3); } } @@ -1322,10 +1348,10 @@ class getid3_riff } $FoundAllChunksWeNeed = true; - fseek($fd, $WhereWeWere, SEEK_SET); + fseek($this->getid3->fp, $WhereWeWere, SEEK_SET); } - fseek($fd, $chunksize - 4, SEEK_CUR); + fseek($this->getid3->fp, $chunksize - 4, SEEK_CUR); //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) { // @@ -1337,8 +1363,8 @@ class getid3_riff $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; - $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize; - if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) { + $LISTchunkMaxOffset = ftell($this->getid3->fp) - 4 + $chunksize; + if ($parsedChunk = $this->ParseRIFF(ftell($this->getid3->fp), ftell($this->getid3->fp) + $chunksize - 4)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } @@ -1347,62 +1373,60 @@ class getid3_riff default: if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { - $nextoffset = ftell($fd) + $chunksize; + $nextoffset = ftell($this->getid3->fp) + $chunksize; if (($nextoffset < 0) || !getid3_lib::intValueSupported($nextoffset)) { - $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; break 2; } - fseek($fd, $nextoffset, SEEK_SET); + fseek($this->getid3->fp, $nextoffset, SEEK_SET); break; } $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } - $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8; + $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($this->getid3->fp) - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': - $ThisFileInfo['avdataoffset'] = ftell($fd); - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize; + $info['avdataoffset'] = ftell($this->getid3->fp); + $info['avdataend'] = $info['avdataoffset'] + $chunksize; - $RIFFdataChunkContentsTest = fread($fd, 36); + $RIFFdataChunkContentsTest = fread($this->getid3->fp, 36); if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { - - $dummy = $ThisFileInfo; // copy info array, only use if there's no error - $getid3_mp3 = new getid3_mp3($fd, $dummy); - $dummy = $ThisFileInfo; // copy info array, only use if there's no error - $getid3_mp3->getOnlyMPEGaudioInfo($fd, $dummy, $RIFFchunk[$chunkname][$thisindex]['offset'], false); - - // use dummy array unless error - if (empty($dummy['error'])) { - $ThisFileInfo = $dummy; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; + $getid3_mp3 = new getid3_mp3($getid3_temp); + $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false); + if (empty($getid3_temp->info['error'])) { + $info['mpeg'] = $getid3_temp->info['mpeg']; + $info['audio'] = $getid3_temp->info['audio']; } + unset($getid3_temp, $getid3_mp3); } } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == "\x0B\x77")) { // This is probably AC-3 data - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; - $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size']; - $dummy['error'] = array(); - - $ac3_tag = new getid3_ac3($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; + $getid3_ac3 = new getid3_ac3($getid3_temp); + $getid3_ac3->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['ac3'] = $getid3_temp->info['ac3']; + $info['warning'] = $getid3_temp->info['warning']; } - unset($ac3_tag); - + unset($getid3_temp, $getid3_ac3); } } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == "\x77\x0B")) { @@ -1411,37 +1435,38 @@ class getid3_riff // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { // ok to use tmpfile here - only 56 bytes - if ($fd_temp = tmpfile()) { - - for ($i = 0; $i < 28; $i += 2) { - // swap byte order - fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); - fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); - } - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = 0; - $dummy['avdataend'] = 20; - $dummy['error'] = array(); - $ac3_tag = new getid3_ac3($fd_temp, $dummy); - fclose($fd_temp); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; + if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) { + if ($fd_temp = fopen($RIFFtempfilename, 'wb')) { + for ($i = 0; $i < 28; $i += 2) { + // swap byte order + fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); + fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); + } + fclose($fd_temp); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($RIFFtempfilename); + $getid3_temp->info['avdataend'] = 20; + $getid3_ac3 = new getid3_ac3($getid3_temp); + $getid3_ac3->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['ac3'] = $getid3_temp->info['ac3']; + $info['warning'] = $getid3_temp->info['warning']; + } else { + $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): '.implode(';', $getid3_temp->info['error']); + } + unset($getid3_ac3, $getid3_temp); } else { - $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: '.explode(';', $dummy['error']); + $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } - unset($ac3_tag); + unlink($RIFFtempfilename); } else { - - $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV'; - + $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } } @@ -1449,9 +1474,9 @@ class getid3_riff } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) { // This is WavPack data - $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; - $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); - getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo); + $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); + $this->RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28)); } else { @@ -1461,10 +1486,10 @@ class getid3_riff } $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize; if (($nextoffset < 0) || !getid3_lib::intValueSupported($nextoffset)) { - $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; break 3; } - fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); + fseek($this->getid3->fp, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); break; case 'bext': @@ -1476,17 +1501,31 @@ class getid3_riff case 'MEXT': case 'DISP': // always read data in - $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); - break; - case 'JUNK': - // never read data in - $nextoffset = ftell($fd) + $chunksize; - if (($nextoffset < 0) || !getid3_lib::intValueSupported($nextoffset)) { - $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; - break 3; + // should be: never read data in + // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc) + if ($chunksize < 1048576) { + if ($chunksize > 0) { + $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); + if ($chunkname == 'JUNK') { + if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) { + // only keep text characters [chr(32)-chr(127)] + $info['riff']['comments']['junk'][] = trim($matches[1]); + } + // but if nothing there, ignore + // remove the key in either case + unset($RIFFchunk[$chunkname][$thisindex]['data']); + } + } + } else { + $info['warning'][] = 'chunk "'.$chunkname.'" at offset '.ftell($this->getid3->fp).' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data'; + $nextoffset = ftell($this->getid3->fp) + $chunksize; + if (($nextoffset < 0) || !getid3_lib::intValueSupported($nextoffset)) { + $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + break 3; + } + fseek($this->getid3->fp, $nextoffset, SEEK_SET); } - fseek($fd, $nextoffset, SEEK_SET); break; default: @@ -1501,17 +1540,17 @@ class getid3_riff if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } - $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize); + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); } elseif (($chunksize > 0) && ($chunksize < 2048)) { // only read data in if smaller than 2kB - $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); + $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); } else { - $nextoffset = ftell($fd) + $chunksize; + $nextoffset = ftell($this->getid3->fp) + $chunksize; if (($nextoffset < 0) || !getid3_lib::intValueSupported($nextoffset)) { - $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; break 3; } - fseek($fd, $nextoffset, SEEK_SET); + fseek($this->getid3->fp, $nextoffset, SEEK_SET); } break; } @@ -1525,9 +1564,9 @@ class getid3_riff } - static function ParseRIFFdata(&$RIFFdata, &$ThisFileInfo) { + function ParseRIFFdata(&$RIFFdata) { + $info = &$this->getid3->info; if ($RIFFdata) { - $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3'); $fp_temp = fopen($tempfile, 'wb'); $RIFFdataLength = strlen($RIFFdata); @@ -1538,18 +1577,26 @@ class getid3_riff fwrite($fp_temp, $RIFFdata); fclose($fp_temp); - $fp_temp = fopen($tempfile, 'rb'); - $dummy = array('filesize'=>$RIFFdataLength, 'filenamepath'=>$ThisFileInfo['filenamepath'], 'tags'=>$ThisFileInfo['tags'], 'avdataoffset'=>0, 'avdataend'=>$RIFFdataLength, 'warning'=>$ThisFileInfo['warning'], 'error'=>$ThisFileInfo['error'], 'comments'=>$ThisFileInfo['comments'], 'audio'=>(isset($ThisFileInfo['audio']) ? $ThisFileInfo['audio'] : array()), 'video'=>(isset($ThisFileInfo['video']) ? $ThisFileInfo['video'] : array())); - $riff = new getid3_riff($fp_temp, $dummy); - $ThisFileInfo['riff'] = $dummy['riff']; - $ThisFileInfo['warning'] = $dummy['warning']; - $ThisFileInfo['error'] = $dummy['error']; - $ThisFileInfo['tags'] = $dummy['tags']; - $ThisFileInfo['comments'] = $dummy['comments']; - unset($riff); - fclose($fp_temp); + $getid3_temp = new getID3(); + $getid3_temp->openfile($tempfile); + $getid3_temp->info['filesize'] = $RIFFdataLength; + $getid3_temp->info['filenamepath'] = $info['filenamepath']; + $getid3_temp->info['tags'] = $info['tags']; + $getid3_temp->info['warning'] = $info['warning']; + $getid3_temp->info['error'] = $info['error']; + $getid3_temp->info['comments'] = $info['comments']; + $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array()); + $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array()); + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->Analyze(); + + $info['riff'] = $getid3_temp->info['riff']; + $info['warning'] = $getid3_temp->info['warning']; + $info['error'] = $getid3_temp->info['error']; + $info['tags'] = $getid3_temp->info['tags']; + $info['comments'] = $getid3_temp->info['comments']; + unset($getid3_riff, $getid3_temp); unlink($tempfile); - return true; } return false; } @@ -1580,7 +1627,7 @@ class getid3_riff } - static function RIFFparseWavPackHeader($WavPackChunkData, &$ThisFileInfo) { + function RIFFparseWavPackHeader($WavPackChunkData) { // typedef struct { // char ckID [4]; // long ckSize; @@ -1592,8 +1639,9 @@ class getid3_riff // } WavpackHeader; // shortcut - $ThisFileInfo['wavpack'] = array(); - $thisfile_wavpack = &$ThisFileInfo['wavpack']; + $info = &$this->getid3->info; + $info['wavpack'] = array(); + $thisfile_wavpack = &$info['wavpack']; $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2)); if ($thisfile_wavpack['version'] >= 2) { @@ -1641,7 +1689,6 @@ class getid3_riff } static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) { - // yes it's ugly to instantiate a getid3_lib object here, suggested alternative please? $getid3_lib = new getid3_lib(); $functionname = ($littleEndian ? 'LittleEndian2Int' : 'BigEndian2Int'); $parsed['biSize'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure @@ -1655,6 +1702,7 @@ class getid3_riff $parsed['biYPelsPerMeter'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 28, 4)); // vertical resolution, in pixels per metre, of the target device $parsed['biClrUsed'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression $parsed['biClrImportant'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important + unset($getid3_lib); return $parsed; } @@ -2016,9 +2064,12 @@ class getid3_riff ETV2 eTreppid Video ETV2 ETVC eTreppid Video ETVC FLIC Autodesk FLI/FLC Animation + FLV1 Sorenson Spark + FLV4 On2 TrueMotion VP6 FRWT Darim Vision Forward Motion JPEG (www.darvision.com) FRWU Darim Vision Forward Uncompressed (www.darvision.com) FLJP D-Vision Field Encoded Motion JPEG + FPS1 FRAPS v1 FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel FRWD SoftLab-Nsk Forward Motion JPEG FVF1 Iterated Systems Fractal Video Frame @@ -2256,6 +2307,7 @@ class getid3_riff VLV1 VideoLogic/PURE Digital Videologic Capture VP30 On2 VP3.0 VP31 On2 VP3.1 + VP6F On2 TrueMotion VP6 VX1K Lucent VX1000S Video Codec VX2K Lucent VX2000S Video Codec VXSP Lucent VX1000SP Video Codec @@ -2303,8 +2355,8 @@ class getid3_riff } - static function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed=false) { - if ($ThisFileInfo['fileformat'] == 'riff') { + function EitherEndian2Int($byteword, $signed=false) { + if ($this->getid3->info['fileformat'] == 'riff') { return getid3_lib::LittleEndian2Int($byteword, $signed); } return getid3_lib::BigEndian2Int($byteword, false, $signed); diff --git a/modules/getid3/module.audio-video.swf.php b/modules/getid3/module.audio-video.swf.php index 43b8641f..a3d49f95 100644 --- a/modules/getid3/module.audio-video.swf.php +++ b/modules/getid3/module.audio-video.swf.php @@ -14,59 +14,51 @@ ///////////////////////////////////////////////////////////////// -class getid3_swf +class getid3_swf extends getid3_handler { + var $ReturnAllTagData = false; - function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { -//$start_time = microtime(true); - $ThisFileInfo['fileformat'] = 'swf'; - $ThisFileInfo['video']['dataformat'] = 'swf'; + function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'swf'; + $info['video']['dataformat'] = 'swf'; // http://www.openswf.org/spec/SWFfileformat.html - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); - $SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data + $SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data - $ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3); - switch ($ThisFileInfo['swf']['header']['signature']) { + $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); + switch ($info['swf']['header']['signature']) { case 'FWS': - $ThisFileInfo['swf']['header']['compressed'] = false; + $info['swf']['header']['compressed'] = false; break; case 'CWS': - $ThisFileInfo['swf']['header']['compressed'] = true; + $info['swf']['header']['compressed'] = true; break; default: - $ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"'; - unset($ThisFileInfo['swf']); - unset($ThisFileInfo['fileformat']); + $info['error'][] = 'Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"'; + unset($info['swf']); + unset($info['fileformat']); return false; break; } - $ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); - $ThisFileInfo['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); - - if ($ThisFileInfo['swf']['header']['compressed']) { + $info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); + $info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); + if ($info['swf']['header']['compressed']) { $SWFHead = substr($SWFfileData, 0, 8); $SWFfileData = substr($SWFfileData, 8); - ob_start(); - if ($decompressed = gzuncompress($SWFfileData)) { - - ob_end_clean(); + if ($decompressed = @gzuncompress($SWFfileData)) { $SWFfileData = $SWFHead.$decompressed; - } else { - - $errormessage = ob_get_contents(); - ob_end_clean(); - $ThisFileInfo['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($ThisFileInfo['swf']['header']['length'] - 8).' bytes uncompressed)'; + $info['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)'; return false; - } - } $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3; @@ -76,8 +68,8 @@ class getid3_swf $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); } list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); - $ThisFileInfo['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); - $ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); + $info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); + $info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm // Next in the header is the frame rate, which is kind of weird. @@ -86,16 +78,16 @@ class getid3_swf // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. // Byte at (8 + $FrameSizeDataLength) is always zero and ignored - $ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); - $ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); + $info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); + $info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate']; - $ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20)); - $ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20)); - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $info['video']['frame_rate'] = $info['swf']['header']['frame_rate']; + $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20)); + $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20)); + $info['video']['pixel_aspect_ratio'] = (float) 1; - if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) { - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate']; + if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) { + $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate']; } //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; @@ -127,13 +119,13 @@ class getid3_swf break 2; case 9: // Set background color - //$ThisFileInfo['swf']['tags'][] = $TagData; - $ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); + //$info['swf']['tags'][] = $TagData; + $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); break; default: - if ($ReturnAllTagData) { - $ThisFileInfo['swf']['tags'][] = $TagData; + if ($this->ReturnAllTagData) { + $info['swf']['tags'][] = $TagData; } break; } diff --git a/modules/getid3/module.audio.aa.php b/modules/getid3/module.audio.aa.php index 77ed4839..39cb77c8 100644 --- a/modules/getid3/module.audio.aa.php +++ b/modules/getid3/module.audio.aa.php @@ -14,40 +14,41 @@ ///////////////////////////////////////////////////////////////// -class getid3_aa +class getid3_aa extends getid3_handler { - function getid3_aa(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AAheader = fread($fd, 8); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $AAheader = fread($this->getid3->fp, 8); $magic = "\x57\x90\x75\x36"; if (substr($AAheader, 4, 4) != $magic) { - $ThisFileInfo['error'][] = 'Expecting "'.PrintHexBytes($magic).'" at offset '.$ThisFileInfo['avdataoffset'].', found "'.PrintHexBytes(substr($AAheader, 4, 4)).'"'; + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AAheader, 4, 4)).'"'; return false; } // shortcut - $ThisFileInfo['aa'] = array(); - $thisfile_au = &$ThisFileInfo['aa']; + $info['aa'] = array(); + $thisfile_au = &$info['aa']; - $ThisFileInfo['fileformat'] = 'aa'; - $ThisFileInfo['audio']['dataformat'] = 'aa'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; // is it? - $thisfile_au['encoding'] = 'ISO-8859-1'; + $info['fileformat'] = 'aa'; + $info['audio']['dataformat'] = 'aa'; + $info['audio']['bitrate_mode'] = 'cbr'; // is it? + $thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4)); - if ($thisfile_au['filesize'] > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { - $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['filesize'].'" bytes of data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"'; + if ($thisfile_au['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) { + $info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'; } - $ThisFileInfo['audio']['bits_per_sample'] = 16; // is it? - $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate']; - $ThisFileInfo['audio']['channels'] = $thisfile_au['channels']; + $info['audio']['bits_per_sample'] = 16; // is it? + $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; + $info['audio']['channels'] = $thisfile_au['channels']; - //$ThisFileInfo['playtime_seconds'] = 0; - //$ThisFileInfo['audio']['bitrate'] = 0; + //$info['playtime_seconds'] = 0; + //$info['audio']['bitrate'] = 0; return true; } diff --git a/modules/getid3/module.audio.aac.php b/modules/getid3/module.audio.aac.php index 269e4215..6bde5ebc 100644 --- a/modules/getid3/module.audio.aac.php +++ b/modules/getid3/module.audio.aac.php @@ -14,28 +14,29 @@ ///////////////////////////////////////////////////////////////// -class getid3_aac +class getid3_aac extends getid3_handler { - - // new combined constructor - function getid3_aac(&$fd, &$ThisFileInfo, $option) { - if ($option === 'adif') { - $this->getAACADIFheaderFilepointer($fd, $ThisFileInfo); - } elseif ($option === 'adts') { - $this->getAACADTSheaderFilepointer($fd, $ThisFileInfo); + function Analyze() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + if (fread($this->getid3->fp, 4) == 'ADIF') { + $this->getAACADIFheaderFilepointer(); + } else { + $this->getAACADTSheaderFilepointer(); } return true; } - function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'aac'; - $ThisFileInfo['audio']['dataformat'] = 'aac'; - $ThisFileInfo['audio']['lossless'] = false; + function getAACADIFheaderFilepointer() { + $info = &$this->getid3->info; + $info['fileformat'] = 'aac'; + $info['audio']['dataformat'] = 'aac'; + $info['audio']['lossless'] = false; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AACheader = fread($fd, 1024); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $AACheader = fread($this->getid3->fp, 1024); $offset = 0; if (substr($AACheader, 0, 4) == 'ADIF') { @@ -63,40 +64,40 @@ class getid3_aac $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader); $bitoffset = 0; - $ThisFileInfo['aac']['header_type'] = 'ADIF'; + $info['aac']['header_type'] = 'ADIF'; $bitoffset += 32; - $ThisFileInfo['aac']['header']['mpeg_version'] = 4; + $info['aac']['header']['mpeg_version'] = 4; - $ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; - if ($ThisFileInfo['aac']['header']['copyright']) { - $ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); + if ($info['aac']['header']['copyright']) { + $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); $bitoffset += 72; } - $ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; - $ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; - $ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; - if ($ThisFileInfo['aac']['header']['is_vbr']) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); + if ($info['aac']['header']['is_vbr']) { + $info['audio']['bitrate_mode'] = 'vbr'; + $info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); + $info['audio']['bitrate_mode'] = 'cbr'; + $info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate']; + $info['audio']['bitrate'] = $info['aac']['header']['bitrate']; } - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; + if ($info['audio']['bitrate'] == 0) { + $info['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; return false; } - $ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) { + for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) { // http://www.audiocoding.com/wiki/index.php?page=program_config_element // buffer_fullness 20 @@ -145,100 +146,100 @@ class getid3_aac // comment_field_data[i] 8 // } - if (!$ThisFileInfo['aac']['header']['is_vbr']) { - $ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); + if (!$info['aac']['header']['is_vbr']) { + $info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); $bitoffset += 20; } - $ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); + $info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); $bitoffset += 3; - $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) { + $info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) { + $info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { + $info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { + $info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { + $info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { + $info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { + $info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { + $info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { + $info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $bitoffset = ceil($bitoffset / 8) * 8; - $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); + $info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); $bitoffset += 8; - $ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'])); - $bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']; + $info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'])); + $bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']; - $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']); - $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']); - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']; - $ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]); - if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) { - $ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field']; + $info['aac']['header']['profile'] = $this->AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']); + $info['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']); + $info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency']; + $info['audio']['channels'] = $this->AACchannelCountCalculate($info['aac']['program_configs'][$i]); + if ($info['aac']['program_configs'][$i]['comment_field']) { + $info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field']; } } - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; + $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']; - $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; + $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile']; @@ -246,9 +247,9 @@ class getid3_aac } else { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['aac']); - $ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)'; + unset($info['fileformat']); + unset($info['aac']); + $info['error'][] = 'AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)'; return false; } @@ -256,25 +257,28 @@ class getid3_aac } - function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { + function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { + $info = &$this->getid3->info; + // based loosely on code from AACfile by Jurgen Faul <jfaulŘgmx.de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - // http://faac.sourceforge.net/wiki/index.php?page=ADTS + // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link + // http://wiki.multimedia.cx/index.php?title=ADTS // * ADTS Fixed Header: these don't change from frame to frame // syncword 12 always: '111111111111' // ID 1 0: MPEG-4, 1: MPEG-2 - // layer 2 always: '00' - // protection_absent 1 - // profile 2 - // sampling_frequency_index 4 - // private_bit 1 + // MPEG layer 2 If you send AAC in MPEG-TS, set to 0 + // protection_absent 1 0: CRC present; 1: no CRC + // profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction) + // sampling_frequency_index 4 15 not allowed + // private_bit 1 usually 0 // channel_configuration 3 - // original/copy 1 - // home 1 - // emphasis 2 only if ID == 0 (ie MPEG-4) + // original/copy 1 0: original; 1: copy + // home 1 usually 0 + // emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation? // * ADTS Variable Header: these can change from frame to frame // copyright_identification_bit 1 @@ -306,160 +310,129 @@ class getid3_aac // or MaxFramesToScan frames have been scanned if (!getid3_lib::intValueSupported($byteoffset)) { - $ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond '.ftell($fd).' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; + $info['warning'][] = 'Unable to parse AAC file beyond '.ftell($this->getid3->fp).' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; return false; } - fseek($fd, $byteoffset, SEEK_SET); + fseek($this->getid3->fp, $byteoffset, SEEK_SET); // First get substring - $substring = fread($fd, 10); + $substring = fread($this->getid3->fp, 9); // header is 7 bytes (or 9 if CRC is present) $substringlength = strlen($substring); - if ($substringlength != 10) { - $ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)'; + if ($substringlength != 9) { + $info['error'][] = 'Failed to read 7 bytes at offset '.(ftell($this->getid3->fp) - $substringlength).' (only read '.$substringlength.' bytes)'; return false; } - - // Initialise $AACheaderBitstream - $AACheaderBitstream = ''; - - // Loop thru substring chars - for ($i = 0; $i < 10; $i++) { - $AACheaderBitstream .= $decbin[$substring{$i}]; - } - - $bitoffset = 0; - - $synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12)); - - $bitoffset += 12; - if ($synctest != 0x0FFF) { - $ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)'; - if ($ThisFileInfo['fileformat'] == 'aac') { + // this would be easier with 64-bit math, but split it up to allow for 32-bit: + $header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2)); + $header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4)); + $header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1)); + + $info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4; + if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) { + $info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($this->getid3->fp) - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)'; + if ($info['fileformat'] == 'aac') { return true; } return false; } // Gather info for first frame only - this takes time to do 1000 times! - if ($framenumber > 0) { - - if (!$AACheaderBitstream[$bitoffset]) { - - // MPEG-4 - $bitoffset += 20; - - } else { - - // MPEG-2 - $bitoffset += 18; - + if ($framenumber == 0) { + $info['aac']['header_type'] = 'ADTS'; + $info['fileformat'] = 'aac'; + $info['audio']['dataformat'] = 'aac'; + + $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3; + $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1; + $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0; + + $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30; + $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26; + $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25; + $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22; + $info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21; + $info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20; + $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19; + $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18; + $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5; + + $info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4); + $info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true); + $info['aac']['header']['profile'] = $this->AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']); + $info['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']); + $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream']; + $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original']; + $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home']; + $info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']); + if ($ReturnExtendedInfo) { + $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream']; + $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start']; } - } else { - - $ThisFileInfo['aac']['header_type'] = 'ADTS'; - $ThisFileInfo['aac']['header']['synch'] = $synctest; - $ThisFileInfo['fileformat'] = 'aac'; - $ThisFileInfo['audio']['dataformat'] = 'aac'; - - $ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - if ($ThisFileInfo['aac']['header']['layer'] != 0) { - $ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead'; - return false; + if ($info['aac']['header']['raw']['mpeg_layer'] != 0) { + $info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead'; } - $ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']); - - $ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']); - if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero'; + if ($info['aac']['header']['sample_frequency'] == 0) { + $info['error'][] = 'Corrupt AAC file: sample_frequency == zero'; return false; } - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency']; - - $ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); - $bitoffset += 3; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration']; - $ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - - if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) { - $ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - } - - if ($ReturnExtendedInfo) { - - $ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - - } else { - - $bitoffset += 2; - - } + $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency']; + $info['audio']['channels'] = $info['aac']['header']['channels']; } - $FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13)); + $FrameLength = ($header2 & 0x0003FFE0) >> 5; if (!isset($BitrateCache[$FrameLength])) { - $BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; + $BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; } - getid3_lib::safe_inc($ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1); - - $ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength; - $bitoffset += 13; - $ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11)); - $bitoffset += 11; - if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1); + + $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength; + + $info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2); + if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) { + $info['audio']['bitrate_mode'] = 'vbr'; } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['bitrate_mode'] = 'cbr'; } - $ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; + $info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0); - if ($ThisFileInfo['aac']['header']['crc_present']) { - //$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16)); - $bitoffset += 16; + if ($info['aac']['header']['crc_present']) { + //$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2); } if (!$ReturnExtendedInfo) { - unset($ThisFileInfo['aac'][$framenumber]); + unset($info['aac'][$framenumber]); + } + + /* + $rounded_precision = 5000; + $info['aac']['bitrate_distribution_rounded'] = array(); + foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) { + $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision; + getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count); } + ksort($info['aac']['bitrate_distribution_rounded']); + */ $byteoffset += $FrameLength; - if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) { + if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) { // keep scanning } else { - $ThisFileInfo['aac']['frames'] = $framenumber; - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; + $info['aac']['frames'] = $framenumber; + $info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds + if ($info['playtime_seconds'] == 0) { + $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; return false; } - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - ksort($ThisFileInfo['aac']['bitrate_distribution']); + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + ksort($info['aac']['bitrate_distribution']); - $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; + $info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile']; return true; @@ -468,7 +441,7 @@ class getid3_aac // should never get here. } - function AACsampleRateLookup($samplerateid) { + static function AACsampleRateLookup($samplerateid) { static $AACsampleRateLookup = array(); if (empty($AACsampleRateLookup)) { $AACsampleRateLookup[0] = 96000; @@ -491,7 +464,7 @@ class getid3_aac return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid'); } - function AACprofileLookup($profileid, $mpegversion) { + static function AACprofileLookup($profileid, $mpegversion) { static $AACprofileLookup = array(); if (empty($AACprofileLookup)) { $AACprofileLookup[2][0] = 'Main profile'; @@ -506,7 +479,7 @@ class getid3_aac return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid'); } - function AACchannelCountCalculate($program_configs) { + static function AACchannelCountCalculate($program_configs) { $channels = 0; for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) { $channels++; diff --git a/modules/getid3/module.audio.ac3.php b/modules/getid3/module.audio.ac3.php index 9809a47c..1472971a 100644 --- a/modules/getid3/module.audio.ac3.php +++ b/modules/getid3/module.audio.ac3.php @@ -14,24 +14,25 @@ ///////////////////////////////////////////////////////////////// -class getid3_ac3 +class getid3_ac3 extends getid3_handler { - function getid3_ac3(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; ///AH - $ThisFileInfo['ac3']['raw']['bsi'] = array(); - $thisfile_ac3 = &$ThisFileInfo['ac3']; + $info['ac3']['raw']['bsi'] = array(); + $thisfile_ac3 = &$info['ac3']; $thisfile_ac3_raw = &$thisfile_ac3['raw']; $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi']; // http://www.atsc.org/standards/a_52a.pdf - $ThisFileInfo['fileformat'] = 'ac3'; - $ThisFileInfo['audio']['dataformat'] = 'ac3'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; + $info['fileformat'] = 'ac3'; + $info['audio']['dataformat'] = 'ac3'; + $info['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['lossless'] = false; // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 @@ -44,13 +45,13 @@ class getid3_ac3 // // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AC3header['syncinfo'] = fread($fd, 5); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $AC3header['syncinfo'] = fread($this->getid3->fp, 5); $thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2); if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") { - $ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead'; + $info['error'][] = 'Expecting "0B 77" at offset '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($AC3header['syncinfo'], 0, 2)).' instead'; unset($thisfile_ac3); return false; @@ -70,14 +71,14 @@ class getid3_ac3 $thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { - $ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; + $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; } $thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); $thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate']; + $info['audio']['bitrate'] = $thisfile_ac3['bitrate']; - $AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15)); + $AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($this->getid3->fp, 15)); $ac3_bsi_offset = 0; $thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); @@ -86,7 +87,7 @@ class getid3_ac3 // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. - $ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'; + $info['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'; unset($thisfile_ac3); return false; } @@ -104,17 +105,17 @@ class getid3_ac3 switch ($thisfile_ac3_raw_bsi['acmod']) { case 0: case 1: - $ThisFileInfo['audio']['channelmode'] = 'mono'; + $info['audio']['channelmode'] = 'mono'; break; case 3: case 4: - $ThisFileInfo['audio']['channelmode'] = 'stereo'; + $info['audio']['channelmode'] = 'stereo'; break; default: - $ThisFileInfo['audio']['channelmode'] = 'surround'; + $info['audio']['channelmode'] = 'surround'; break; } - $ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels']; + $info['audio']['channels'] = $thisfile_ac3['num_channels']; if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) { // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. @@ -141,8 +142,8 @@ class getid3_ac3 $ac3_bsi_offset += 1; $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon']; if ($thisfile_ac3_raw_bsi['lfeon']) { - //$ThisFileInfo['audio']['channels']++; - $ThisFileInfo['audio']['channels'] .= '.1'; + //$info['audio']['channels']++; + $info['audio']['channels'] .= '.1'; } $thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); @@ -247,7 +248,7 @@ class getid3_ac3 $thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6)); $ac3_bsi_offset += 6; - $AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length'])); + $AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($this->getid3->fp, $thisfile_ac3_raw_bsi['addbsi_length'])); $thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); $ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; diff --git a/modules/getid3/module.audio.au.php b/modules/getid3/module.audio.au.php index afbc75d6..a1094dbc 100644 --- a/modules/getid3/module.audio.au.php +++ b/modules/getid3/module.audio.au.php @@ -14,31 +14,33 @@ ///////////////////////////////////////////////////////////////// -class getid3_au +class getid3_au extends getid3_handler { - function getid3_au(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AUheader = fread($fd, 8); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $AUheader = fread($this->getid3->fp, 8); - if (substr($AUheader, 0, 4) != '.snd') { - $ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"'; + $magic = '.snd'; + if (substr($AUheader, 0, 4) != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" (".snd") at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AUheader, 0, 4)).'"'; return false; } // shortcut - $ThisFileInfo['au'] = array(); - $thisfile_au = &$ThisFileInfo['au']; + $info['au'] = array(); + $thisfile_au = &$info['au']; - $ThisFileInfo['fileformat'] = 'au'; - $ThisFileInfo['audio']['dataformat'] = 'au'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $info['fileformat'] = 'au'; + $info['audio']['dataformat'] = 'au'; + $info['audio']['bitrate_mode'] = 'cbr'; $thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4)); - $AUheader .= fread($fd, $thisfile_au['header_length'] - 8); - $ThisFileInfo['avdataoffset'] += $thisfile_au['header_length']; + $AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8); + $info['avdataoffset'] += $thisfile_au['header_length']; $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4)); $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4)); @@ -49,20 +51,20 @@ class getid3_au $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; + $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; } else { unset($thisfile_au['bits_per_sample']); } - $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate']; - $ThisFileInfo['audio']['channels'] = $thisfile_au['channels']; + $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; + $info['audio']['channels'] = $thisfile_au['channels']; - if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"'; + if (($info['avdataoffset'] + $thisfile_au['data_size']) > $info['avdataend']) { + $info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'; } - $ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); + $info['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $info['playtime_seconds']; return true; } diff --git a/modules/getid3/module.audio.avr.php b/modules/getid3/module.audio.avr.php index 60994397..9c6d6650 100644 --- a/modules/getid3/module.audio.avr.php +++ b/modules/getid3/module.audio.avr.php @@ -14,10 +14,11 @@ ///////////////////////////////////////////////////////////////// -class getid3_avr +class getid3_avr extends getid3_handler { - function getid3_avr(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html @@ -59,61 +60,62 @@ class getid3_avr // 8-bit data between signed/unsigned just add 127 to the sample values. // Simularly for 16-bit data you should add 32769 - $ThisFileInfo['fileformat'] = 'avr'; + $info['fileformat'] = 'avr'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AVRheader = fread($fd, 128); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $AVRheader = fread($this->getid3->fp, 128); - $ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4); - if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') { - $ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['avr']); + $info['avr']['raw']['magic'] = substr($AVRheader, 0, 4); + $magic = '2BIT'; + if ($info['avr']['raw']['magic'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['avr']['raw']['magic']).'"'; + unset($info['fileformat']); + unset($info['avr']); return false; } - $ThisFileInfo['avdataoffset'] += 128; - - $ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); - $ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2)); - $ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2)); - $ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2)); - $ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2)); - $ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2)); - $ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1)); - $ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3)); - $ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4)); - $ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4)); - $ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4)); - $ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2)); - $ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2)); - $ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2)); - $ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); - $ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); - - $ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true); - $ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true); - $ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true); - - $ThisFileInfo['avr']['midi_notes'] = array(); - if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) { - $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8; + $info['avdataoffset'] += 128; + + $info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); + $info['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2)); + $info['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2)); + $info['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2)); + $info['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2)); + $info['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2)); + $info['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1)); + $info['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3)); + $info['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4)); + $info['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4)); + $info['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4)); + $info['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2)); + $info['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2)); + $info['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2)); + $info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); + $info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); + + $info['avr']['flags']['stereo'] = (($info['avr']['raw']['mono'] == 0) ? false : true); + $info['avr']['flags']['signed'] = (($info['avr']['raw']['signed'] == 0) ? false : true); + $info['avr']['flags']['loop'] = (($info['avr']['raw']['loop'] == 0) ? false : true); + + $info['avr']['midi_notes'] = array(); + if (($info['avr']['raw']['midi'] & 0xFF00) != 0xFF00) { + $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xFF00) >> 8; } - if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) { - $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF); + if (($info['avr']['raw']['midi'] & 0x00FF) != 0x00FF) { + $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0x00FF); } - if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) { - $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + if (($info['avdataend'] - $info['avdataoffset']) != ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2))) { + $info['warning'][] = 'Probable truncated file: expecting '.($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']); } - $ThisFileInfo['audio']['dataformat'] = 'avr'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate']; - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1); - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate']; - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds']; + $info['audio']['dataformat'] = 'avr'; + $info['audio']['lossless'] = true; + $info['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample']; + $info['audio']['sample_rate'] = $info['avr']['sample_rate']; + $info['audio']['channels'] = ($info['avr']['flags']['stereo'] ? 2 : 1); + $info['playtime_seconds'] = ($info['avr']['sample_length'] / $info['audio']['channels']) / $info['avr']['sample_rate']; + $info['audio']['bitrate'] = ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $info['playtime_seconds']; return true; diff --git a/modules/getid3/module.audio.bonk.php b/modules/getid3/module.audio.bonk.php index cab2ff59..9f5187e3 100644 --- a/modules/getid3/module.audio.bonk.php +++ b/modules/getid3/module.audio.bonk.php @@ -14,67 +14,68 @@ ///////////////////////////////////////////////////////////////// -class getid3_bonk +class getid3_bonk extends getid3_handler { - function getid3_bonk(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // shortcut - $ThisFileInfo['bonk'] = array(); - $thisfile_bonk = &$ThisFileInfo['bonk']; + $info['bonk'] = array(); + $thisfile_bonk = &$info['bonk']; - $thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset']; - $thisfile_bonk['dataend'] = $ThisFileInfo['avdataend']; + $thisfile_bonk['dataoffset'] = $info['avdataoffset']; + $thisfile_bonk['dataend'] = $info['avdataend']; if (!getid3_lib::intValueSupported($thisfile_bonk['dataend'])) { - $ThisFileInfo['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to '.round(PHP_INT_MAX / 1073741824).'GB'; + $info['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to '.round(PHP_INT_MAX / 1073741824).'GB'; } else { // scan-from-end method, for v0.6 and higher - fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET); - $PossibleBonkTag = fread($fd, 8); + fseek($this->getid3->fp, $thisfile_bonk['dataend'] - 8, SEEK_SET); + $PossibleBonkTag = fread($this->getid3->fp, 8); while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) { $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4)); - fseek($fd, 0 - $BonkTagSize, SEEK_CUR); - $BonkTagOffset = ftell($fd); - $TagHeaderTest = fread($fd, 5); + fseek($this->getid3->fp, 0 - $BonkTagSize, SEEK_CUR); + $BonkTagOffset = ftell($this->getid3->fp); + $TagHeaderTest = fread($this->getid3->fp, 5); if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) { - $ThisFileInfo['error'][] = 'Expecting "Ř'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"'; + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"'; return false; } $BonkTagName = substr($TagHeaderTest, 1, 4); $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize; $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + $this->HandleBonkTags($BonkTagName); $NextTagEndOffset = $BonkTagOffset - 8; if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) { - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + if (empty($info['audio']['encoder'])) { + $info['audio']['encoder'] = 'Extended BONK v0.9+'; } return true; } - fseek($fd, $NextTagEndOffset, SEEK_SET); - $PossibleBonkTag = fread($fd, 8); + fseek($this->getid3->fp, $NextTagEndOffset, SEEK_SET); + $PossibleBonkTag = fread($this->getid3->fp, 8); } } // seek-from-beginning method for v0.4 and v0.5 if (empty($thisfile_bonk['BONK'])) { - fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET); + fseek($this->getid3->fp, $thisfile_bonk['dataoffset'], SEEK_SET); do { - $TagHeaderTest = fread($fd, 5); + $TagHeaderTest = fread($this->getid3->fp, 5); switch ($TagHeaderTest) { case "\x00".'BONK': - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'BONK v0.4'; + if (empty($info['audio']['encoder'])) { + $info['audio']['encoder'] = 'BONK v0.4'; } break; case "\x00".'INFO': - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5'; + $info['audio']['encoder'] = 'Extended BONK v0.5'; break; default: @@ -83,43 +84,43 @@ class getid3_bonk $BonkTagName = substr($TagHeaderTest, 1, 4); $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + $this->HandleBonkTags($BonkTagName); } while (true); } // parse META block for v0.6 - v0.8 if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) { - fseek($fd, $thisfile_bonk['META']['tags']['info'], SEEK_SET); - $TagHeaderTest = fread($fd, 5); + fseek($this->getid3->fp, $thisfile_bonk['META']['tags']['info'], SEEK_SET); + $TagHeaderTest = fread($this->getid3->fp, 5); if ($TagHeaderTest == "\x00".'INFO') { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; + $info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; $BonkTagName = substr($TagHeaderTest, 1, 4); $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + $this->HandleBonkTags($BonkTagName); } } - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + if (empty($info['audio']['encoder'])) { + $info['audio']['encoder'] = 'Extended BONK v0.9+'; } if (empty($thisfile_bonk['BONK'])) { - unset($ThisFileInfo['bonk']); + unset($info['bonk']); } return true; } - function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) { - + function HandleBonkTags($BonkTagName) { + $info = &$this->getid3->info; switch ($BonkTagName) { case 'BONK': // shortcut - $thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK']; + $thisfile_bonk_BONK = &$info['bonk']['BONK']; - $BonkData = "\x00".'BONK'.fread($fd, 17); + $BonkData = "\x00".'BONK'.fread($this->getid3->fp, 17); $thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); $thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4)); $thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4)); @@ -131,40 +132,40 @@ class getid3_bonk $thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1)); $thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2)); - $ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; - $ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; - - $ThisFileInfo['fileformat'] = 'bonk'; - $ThisFileInfo['audio']['dataformat'] = 'bonk'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // assumed - $ThisFileInfo['audio']['channels'] = $thisfile_bonk_BONK['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; - $ThisFileInfo['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'); - $ThisFileInfo['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; - $ThisFileInfo['audio']['codec'] = 'bonk'; - - $ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); - if ($ThisFileInfo['playtime_seconds'] > 0) { - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; + $info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; + + $info['fileformat'] = 'bonk'; + $info['audio']['dataformat'] = 'bonk'; + $info['audio']['bitrate_mode'] = 'vbr'; // assumed + $info['audio']['channels'] = $thisfile_bonk_BONK['channels']; + $info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; + $info['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'); + $info['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; + $info['audio']['codec'] = 'bonk'; + + $info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); + if ($info['playtime_seconds'] > 0) { + $info['audio']['bitrate'] = (($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8) / $info['playtime_seconds']; } break; case 'INFO': // shortcut - $thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO']; + $thisfile_bonk_INFO = &$info['bonk']['INFO']; - $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); + $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); $thisfile_bonk_INFO['entries_count'] = 0; - $NextInfoDataPair = fread($fd, 5); + $NextInfoDataPair = fread($this->getid3->fp, 5); if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { - while (!feof($fd)) { + while (!feof($this->getid3->fp)) { //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); //$thisfile_bonk_INFO[] = $CurrentSeekInfo; - $NextInfoDataPair = fread($fd, 5); + $NextInfoDataPair = fread($this->getid3->fp, 5); if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { - fseek($fd, -5, SEEK_CUR); + fseek($this->getid3->fp, -5, SEEK_CUR); break; } $thisfile_bonk_INFO['entries_count']++; @@ -173,37 +174,45 @@ class getid3_bonk break; case 'META': - $BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5); - $ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); + $BonkData = "\x00".'META'.fread($this->getid3->fp, $info['bonk']['META']['size'] - 5); + $info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ŘMETA $offset = 6; for ($i = 0; $i < $MetaTagEntries; $i++) { - $MetaEntryTagName = substr($BonkData, $offset, 4); + $MetaEntryTagName = substr($BonkData, $offset, 4); $offset += 4; $MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4)); $offset += 4; - $ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; + $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; } break; case ' ID3': - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + $info['audio']['encoder'] = 'Extended BONK v0.9+'; // ID3v2 checking is optional if (class_exists('getid3_id3v2')) { - $ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_id3v2 = new getid3_id3v2($getid3_temp); + $getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2; + $info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze(); + if ($info['bonk'][' ID3']['valid']) { + $info['id3v2'] = $getid3_temp->info['id3v2']; + } + unset($getid3_temp, $getid3_id3v2); } break; default: - $ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset']; + $info['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$info['bonk'][$BonkTagName]['offset']; break; } } - function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { + static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); foreach ($BonkIsValidTagName as $validtagname) { if ($validtagname == $PossibleBonkTag) { diff --git a/modules/getid3/module.audio.dss.php b/modules/getid3/module.audio.dss.php index 030f14a7..b7b43676 100644 --- a/modules/getid3/module.audio.dss.php +++ b/modules/getid3/module.audio.dss.php @@ -14,28 +14,29 @@ ///////////////////////////////////////////////////////////////// -class getid3_dss +class getid3_dss extends getid3_handler { - function getid3_dss(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $DSSheader = fread($fd, 1256); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $DSSheader = fread($this->getid3->fp, 1256); - if (!preg_match('#^(\x02|\x03)dss', $DSSheader)) { - $ThisFileInfo['error'][] = 'Expecting "[x02-x03]dss" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($DSSheader, 0, 4).'"'; + if (!preg_match('#^(\x02|\x03)dss#', $DSSheader)) { + $info['error'][] = 'Expecting "[02-03] 64 73 73" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"'; return false; } // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm // shortcut - $ThisFileInfo['dss'] = array(); - $thisfile_dss = &$ThisFileInfo['dss']; + $info['dss'] = array(); + $thisfile_dss = &$info['dss']; - $ThisFileInfo['fileformat'] = 'dss'; - $ThisFileInfo['audio']['dataformat'] = 'dss'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $info['fileformat'] = 'dss'; + $info['audio']['dataformat'] = 'dss'; + $info['audio']['bitrate_mode'] = 'cbr'; //$thisfile_dss['encoding'] = 'ISO-8859-1'; $thisfile_dss['version'] = ord(substr($DSSheader, 0, 1)); @@ -47,12 +48,12 @@ class getid3_dss $thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100)); - //$ThisFileInfo['audio']['bits_per_sample'] = ?; - //$ThisFileInfo['audio']['sample_rate'] = ?; - $ThisFileInfo['audio']['channels'] = 1; + //$info['audio']['bits_per_sample'] = ?; + //$info['audio']['sample_rate'] = ?; + $info['audio']['channels'] = 1; - $ThisFileInfo['playtime_seconds'] = $thisfile_dss['length']; - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['filesize'] * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $thisfile_dss['length']; + $info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds']; return true; } diff --git a/modules/getid3/module.audio.dts.php b/modules/getid3/module.audio.dts.php index 17488012..2e5eeff3 100644 --- a/modules/getid3/module.audio.dts.php +++ b/modules/getid3/module.audio.dts.php @@ -14,78 +14,81 @@ ///////////////////////////////////////////////////////////////// -class getid3_dts +class getid3_dts extends getid3_handler { - function getid3_dts(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; + // Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)" // (http://pda.etsi.org/pda/queryform.asp) // With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/ - $ThisFileInfo['fileformat'] = 'dts'; + $info['fileformat'] = 'dts'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $DTSheader = fread($fd, 16); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $DTSheader = fread($this->getid3->fp, 16); - $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4)); - if ($ThisFileInfo['dts']['raw']['magic'] != 0x7FFE8001) { - $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset '.$ThisFileInfo['avdataoffset'].', found "0x'.str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT).'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['dts']); + $info['dts']['raw']['magic'] = substr($DTSheader, 0, 4); + $magic = "\x7F\xFE\x80\x01"; + if ($info['dts']['raw']['magic'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dts']['raw']['magic']).'"'; + unset($info['fileformat']); + unset($info['dts']); return false; } $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12)); $bsOffset = 0; - $ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; - $ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7; - $ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14; - $ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6)); $bsOffset += 6; - $ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; - $ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; - $ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3)); $bsOffset += 3; - $ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; - $ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - if ($ThisFileInfo['dts']['flags']['crc_present']) { - $ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16; + $info['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; + $info['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7; + $info['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14; + $info['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6)); $bsOffset += 6; + $info['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; + $info['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5; + $info['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3)); $bsOffset += 3; + $info['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; + $info['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + if ($info['dts']['flags']['crc_present']) { + $info['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16; } - $ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; - $ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; - $ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; - $ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; - $ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; - - - $ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']); - $ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']); - $ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']); - $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']); - $ThisFileInfo['dts']['flags']['lossless'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 31) ? true : false); - $ThisFileInfo['dts']['bitrate_mode'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); - $ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']); - $ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']); - - $ThisFileInfo['audio']['dataformat'] = 'dts'; - $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless']; - $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels']; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate']; - if (isset($ThisFileInfo['avdataend'])) { - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8); + $info['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; + $info['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; + $info['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2; + $info['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1; + $info['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4; + + + $info['dts']['bitrate'] = $this->DTSbitrateLookup($info['dts']['raw']['bitrate']); + $info['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($info['dts']['raw']['bits_per_sample']); + $info['dts']['sample_rate'] = $this->DTSsampleRateLookup($info['dts']['raw']['sample_frequency']); + $info['dts']['dialog_normalization'] = $this->DTSdialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); + $info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false); + $info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); + $info['dts']['channels'] = $this->DTSnumChannelsLookup($info['dts']['raw']['channel_arrangement']); + $info['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($info['dts']['raw']['channel_arrangement']); + + $info['audio']['dataformat'] = 'dts'; + $info['audio']['lossless'] = $info['dts']['flags']['lossless']; + $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode']; + $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample']; + $info['audio']['sample_rate'] = $info['dts']['sample_rate']; + $info['audio']['channels'] = $info['dts']['channels']; + $info['audio']['bitrate'] = $info['dts']['bitrate']; + if (isset($info['avdataend'])) { + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); } return true; diff --git a/modules/getid3/module.audio.flac.php b/modules/getid3/module.audio.flac.php index 60d7f78c..3857baf2 100644 --- a/modules/getid3/module.audio.flac.php +++ b/modules/getid3/module.audio.flac.php @@ -16,59 +16,68 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); -class getid3_flac +class getid3_flac extends getid3_handler { + var $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 + + function Analyze() { + $info = &$this->getid3->info; - function getid3_flac(&$fd, &$ThisFileInfo) { // http://flac.sourceforge.net/format.html - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $StreamMarker = fread($fd, 4); - if ($StreamMarker != 'fLaC') { - $ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $StreamMarker = fread($this->getid3->fp, 4); + $magic = 'fLaC'; + if ($StreamMarker != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"'; return false; } - $ThisFileInfo['fileformat'] = 'flac'; - $ThisFileInfo['audio']['dataformat'] = 'flac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; + $info['fileformat'] = 'flac'; + $info['audio']['dataformat'] = 'flac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; - return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo); + return $this->FLACparseMETAdata(); } - static function FLACparseMETAdata(&$fd, &$ThisFileInfo) { - + function FLACparseMETAdata() { + $info = &$this->getid3->info; do { - $METAdataBlockOffset = ftell($fd); - $METAdataBlockHeader = fread($fd, 4); + $METAdataBlockOffset = ftell($this->getid3->fp); + $METAdataBlockHeader = fread($this->getid3->fp, 4); $METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80); $METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F; $METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3)); $METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType); if ($METAdataBlockLength < 0) { - $ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; + $info['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; break; } - $ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array(); - $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw']; + $info['flac'][$METAdataBlockTypeText]['raw'] = array(); + $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$info['flac'][$METAdataBlockTypeText]['raw']; $ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset; $ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag; $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType; $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText; $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength; - ob_start(); - $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = fread($fd, $METAdataBlockLength); - $errormessage = ob_get_contents(); - ob_end_clean(); - $ThisFileInfo['avdataoffset'] = ftell($fd); + if (($METAdataBlockOffset + 4 + $METAdataBlockLength) > $info['filesize']) { + $info['error'][] = 'METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset.' extends beyond end of file'; + break; + } + if ($METAdataBlockLength < 1) { + $info['error'][] = 'METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$METAdataBlockLength.') at offset '.$METAdataBlockOffset.' is invalid'; + break; + } + $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = fread($this->getid3->fp, $METAdataBlockLength); + $info['avdataoffset'] = ftell($this->getid3->fp); switch ($METAdataBlockTypeText) { case 'STREAMINFO': // 0x00 - if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + if (!$this->FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) { return false; } break; @@ -78,85 +87,105 @@ class getid3_flac break; case 'APPLICATION': // 0x02 - if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + if (!$this->FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) { return false; } break; case 'SEEKTABLE': // 0x03 - if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + if (!$this->FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) { return false; } break; case 'VORBIS_COMMENT': // 0x04 - $OldOffset = ftell($fd); - fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR); - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); - fseek($fd, $OldOffset, SEEK_SET); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - $METAdataBlockLength; + $getid3_temp->info['audio']['dataformat'] = 'flac'; + $getid3_temp->info['flac'] = $info['flac']; + $getid3_ogg = new getid3_ogg($getid3_temp); + $getid3_ogg->ParseVorbisCommentsFilepointer(); + $maybe_copy_keys = array('vendor', 'comments_raw', 'comments', 'replay_gain'); + foreach ($maybe_copy_keys as $maybe_copy_key) { + if (!empty($getid3_temp->info['ogg'][$maybe_copy_key])) { + $info['ogg'][$maybe_copy_key] = $getid3_temp->info['ogg'][$maybe_copy_key]; + } + } + if (!empty($getid3_temp->info['replay_gain'])) { + $info['replay_gain'] = $getid3_temp->info['replay_gain']; + } + unset($getid3_temp, $getid3_ogg); break; case 'CUESHEET': // 0x05 - if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) { return false; } break; case 'PICTURE': // 0x06 - if (!getid3_flac::FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + if (!getid3_flac::FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) { return false; } break; default: - $ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; + $info['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; break; } } while ($METAdataLastBlockFlag === false); + if (isset($info['flac']['PICTURE'])) { + foreach ($info['flac']['PICTURE'] as $key => $valuearray) { + if (!empty($valuearray['image_mime']) && !empty($valuearray['data'])) { + $info['ogg']['comments']['picture'][] = array('image_mime'=>$valuearray['image_mime'], 'data'=>$valuearray['data']); + } + } + } - if (isset($ThisFileInfo['flac']['STREAMINFO'])) { - $ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; - $ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8); - if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero'; + if (isset($info['flac']['STREAMINFO'])) { + $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset']; + $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8); + if ($info['flac']['uncompressed_audio_bytes'] == 0) { + $info['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero'; return false; } - $ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes']; + $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes']; } // set md5_data_source - built into flac 0.5+ - if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) { + if (isset($info['flac']['STREAMINFO']['audio_signature'])) { - if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) { + if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) { - $ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)'; + $info['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)'; } else { - $ThisFileInfo['md5_data_source'] = ''; - $md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature']; + $info['md5_data_source'] = ''; + $md5 = $info['flac']['STREAMINFO']['audio_signature']; for ($i = 0; $i < strlen($md5); $i++) { - $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); + $info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); } - if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { - unset($ThisFileInfo['md5_data_source']); + if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) { + unset($info['md5_data_source']); } } } - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; - if ($ThisFileInfo['audio']['bits_per_sample'] == 8) { + $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample']; + if ($info['audio']['bits_per_sample'] == 8) { // special case // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed - $ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file'; + $info['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file'; } - if (!empty($ThisFileInfo['ogg']['vendor'])) { - $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor']; + if (!empty($info['ogg']['vendor'])) { + $info['audio']['encoder'] = $info['ogg']['vendor']; } return true; @@ -213,66 +242,67 @@ class getid3_flac return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved'); } - static function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) { + function FLACparseSTREAMINFO($METAdataBlockData) { + $info = &$this->getid3->info; + $offset = 0; - $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $info['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; - $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $info['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; - $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); + $info['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; - $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); + $info['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8)); - $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); - $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; - $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; - $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); + $info['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); + $info['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; + $info['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; + $info['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); $offset += 8; - $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); + $info['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); $offset += 16; - if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) { + if (!empty($info['flac']['STREAMINFO']['sample_rate'])) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; - if ($ThisFileInfo['playtime_seconds'] > 0) { - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $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']; + if ($info['playtime_seconds'] > 0) { + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } } else { - - $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO'; + $info['error'][] = 'Corrupt METAdata block: STREAMINFO'; return false; - } - - unset($ThisFileInfo['flac']['STREAMINFO']['raw']); - + unset($info['flac']['STREAMINFO']['raw']); return true; } - static function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) { + function FLACparseAPPLICATION($METAdataBlockData) { + $info = &$this->getid3->info; + $offset = 0; $ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4)); $offset += 4; - $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID); - $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset); + $info['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID); + $info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset); $offset = $METAdataBlockLength; - unset($ThisFileInfo['flac']['APPLICATION']['raw']); - + unset($info['flac']['APPLICATION']['raw']); return true; } - static function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) { + function FLACparseSEEKTABLE($METAdataBlockData) { + $info = &$this->getid3->info; + $offset = 0; $METAdataBlockLength = strlen($METAdataBlockData); $placeholderpattern = str_repeat("\xFF", 8); @@ -282,61 +312,62 @@ class getid3_flac if ($SampleNumberString == $placeholderpattern) { // placeholder point - getid3_lib::safe_inc($ThisFileInfo['flac']['SEEKTABLE']['placeholders'], 1); + getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1); $offset += 10; } else { $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString); - $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; - $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; } } - unset($ThisFileInfo['flac']['SEEKTABLE']['raw']); + unset($info['flac']['SEEKTABLE']['raw']); return true; } - static function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) { + function FLACparseCUESHEET($METAdataBlockData) { + $info = &$this->getid3->info; $offset = 0; - $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0"); + $info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0"); $offset += 128; - $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; - $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80); + $info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80); $offset += 1; $offset += 258; // reserved - $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; - for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) { + for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) { $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; $TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12); + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12); $offset += 12; - $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); $offset += 13; // reserved - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; - for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { + for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; $IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); @@ -344,18 +375,21 @@ class getid3_flac $offset += 3; // reserved - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; } } - unset($ThisFileInfo['flac']['CUESHEET']['raw']); + unset($info['flac']['CUESHEET']['raw']); return true; } - static function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) { - $picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1]; + function FLACparsePICTURE($meta_data_block_data) { + $info = &$this->getid3->info; + $picture = &$info['flac']['PICTURE'][sizeof($info['flac']['PICTURE']) - 1]; + $picture['offset'] = $info['flac']['PICTURE']['raw']['offset']; + unset($info['flac']['PICTURE']['raw']); $offset = 0; @@ -366,7 +400,7 @@ class getid3_flac $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)); $offset += 4; - $picture['mime_type'] = substr($meta_data_block_data, $offset, $length); + $picture['image_mime'] = substr($meta_data_block_data, $offset, $length); $offset += $length; $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)); @@ -390,11 +424,54 @@ class getid3_flac $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)); $offset += 4; - $picture['image_data'] = substr($meta_data_block_data, $offset, $length); + $picture['data'] = substr($meta_data_block_data, $offset, $length); $offset += $length; - $picture['data_length'] = strlen($picture['image_data']); + $picture['data_length'] = strlen($picture['data']); + + + do { + if ($this->inline_attachments === false) { + // skip entirely + unset($picture['data']); + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $picture['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$picture['offset'].' is too large to process inline ('.number_format($picture['data_length']).' bytes)'; + unset($picture['data']); + break; + } + } 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)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$picture['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; + unset($picture['data']); + break; + } + } + // if we get this far, must be OK + if (is_string($this->inline_attachments)) { + $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$picture['offset']; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $picture['data']); + } else { + $info['warning'][] = 'attachment at '.$picture['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $picture['data_filename'] = $destination_filename; + unset($picture['data']); + } else { + if (!isset($info['flac']['comments']['picture'])) { + $info['flac']['comments']['picture'] = array(); + } + $info['flac']['comments']['picture'][] = array('data'=>$picture['data'], 'image_mime'=>$picture['image_mime']); + } + } while (false); + - unset($ThisFileInfo['flac']['PICTURE']['raw']); return true; } diff --git a/modules/getid3/module.audio.la.php b/modules/getid3/module.audio.la.php index fe2caa64..98d80a6d 100644 --- a/modules/getid3/module.audio.la.php +++ b/modules/getid3/module.audio.la.php @@ -15,99 +15,101 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -class getid3_la +class getid3_la extends getid3_handler { - function getid3_la(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; + $offset = 0; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $rawdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); switch (substr($rawdata, $offset, 4)) { case 'LA02': case 'LA03': case 'LA04': - $ThisFileInfo['fileformat'] = 'la'; - $ThisFileInfo['audio']['dataformat'] = 'la'; - $ThisFileInfo['audio']['lossless'] = true; + $info['fileformat'] = 'la'; + $info['audio']['dataformat'] = 'la'; + $info['audio']['lossless'] = true; - $ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); - $ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); - $ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10); + $info['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); + $info['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); + $info['la']['version'] = (float) $info['la']['version_major'] + ($info['la']['version_minor'] / 10); $offset += 4; - $ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - if ($ThisFileInfo['la']['uncompressed_size'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero'; + if ($info['la']['uncompressed_size'] == 0) { + $info['error'][] = 'Corrupt LA file: uncompressed_size == zero'; return false; } $WAVEchunk = substr($rawdata, $offset, 4); if ($WAVEchunk !== 'WAVE') { - $ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.'; + $info['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.'; return false; } $offset += 4; - $ThisFileInfo['la']['fmt_size'] = 24; - if ($ThisFileInfo['la']['version'] >= 0.3) { + $info['la']['fmt_size'] = 24; + if ($info['la']['version'] >= 0.3) { - $ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24; + $info['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['header_size'] = 49 + $info['la']['fmt_size'] - 24; $offset += 4; } else { // version 0.2 didn't support additional data blocks - $ThisFileInfo['la']['header_size'] = 41; + $info['la']['header_size'] = 41; } $fmt_chunk = substr($rawdata, $offset, 4); if ($fmt_chunk !== 'fmt ') { - $ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'; + $info['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'; return false; } $offset += 4; $fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - $ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; - $ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $info['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; - if ($ThisFileInfo['la']['channels'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero'; + if ($info['la']['channels'] == 0) { + $info['error'][] = 'Corrupt LA file: channels == zero'; return false; } - $ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - if ($ThisFileInfo['la']['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero'; + if ($info['la']['sample_rate'] == 0) { + $info['error'][] = 'Corrupt LA file: sample_rate == zero'; return false; } - $ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - $ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $info['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; - $ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $info['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; - $ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - $ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1)); + $info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1)); $offset += 1; - $ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01); - if ($ThisFileInfo['la']['version'] >= 0.4) { - $ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02); + $info['la']['flags']['seekable'] = (bool) ($info['la']['raw']['flags'] & 0x01); + if ($info['la']['version'] >= 0.4) { + $info['la']['flags']['high_compression'] = (bool) ($info['la']['raw']['flags'] & 0x02); } - $ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; // mikeŘbevin*de @@ -116,108 +118,107 @@ class getid3_la // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should // give the number of bytes used for the seekpoints. Of course, if seeking // is disabled, there are no seekpoints stored. - if ($ThisFileInfo['la']['version'] >= 0.4) { - $ThisFileInfo['la']['blocksize'] = 61440; - $ThisFileInfo['la']['seekevery'] = 19; + if ($info['la']['version'] >= 0.4) { + $info['la']['blocksize'] = 61440; + $info['la']['seekevery'] = 19; } else { - $ThisFileInfo['la']['blocksize'] = 73728; - $ThisFileInfo['la']['seekevery'] = 16; + $info['la']['blocksize'] = 73728; + $info['la']['seekevery'] = 16; } - $ThisFileInfo['la']['seekpoint_count'] = 0; - if ($ThisFileInfo['la']['flags']['seekable']) { - $ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery'])); + $info['la']['seekpoint_count'] = 0; + if ($info['la']['flags']['seekable']) { + $info['la']['seekpoint_count'] = floor($info['la']['samples'] / ($info['la']['blocksize'] * $info['la']['seekevery'])); - for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) { - $ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + for ($i = 0; $i < $info['la']['seekpoint_count']; $i++) { + $info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; } } - if ($ThisFileInfo['la']['version'] >= 0.3) { + if ($info['la']['version'] >= 0.3) { // Following the main header information, the program outputs all of the // seekpoints. Following these is what I called the 'footer start', // i.e. the position immediately after the La audio data is finished. - $ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; - if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')'; - $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize']; + if ($info['la']['footerstart'] > $info['filesize']) { + $info['warning'][] = 'FooterStart value points to offset '.$info['la']['footerstart'].' which is beyond end-of-file ('.$info['filesize'].')'; + $info['la']['footerstart'] = $info['filesize']; } } else { // La v0.2 didn't have FooterStart value - $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend']; + $info['la']['footerstart'] = $info['avdataend']; } - if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { + if ($info['la']['footerstart'] < $info['avdataend']) { if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) { if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) { $RIFFdata = 'WAVE'; - if ($ThisFileInfo['la']['version'] == 0.2) { + if ($info['la']['version'] == 0.2) { $RIFFdata .= substr($rawdata, 12, 24); } else { $RIFFdata .= substr($rawdata, 16, 24); } - if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { - fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET); - $RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']); + if ($info['la']['footerstart'] < $info['avdataend']) { + fseek($this->getid3->fp, $info['la']['footerstart'], SEEK_SET); + $RIFFdata .= fread($this->getid3->fp, $info['avdataend'] - $info['la']['footerstart']); } $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata; fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); - $dummy = $ThisFileInfo; - $dummy['filesize'] = strlen($RIFFdata); - $dummy['avdataoffset'] = 0; - $dummy['avdataend'] = $dummy['filesize']; - - $riff = new getid3_riff($RIFF_fp, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['riff'] = $dummy['riff']; + fclose($RIFF_fp); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($RIFFtempfilename); + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->Analyze(); + + if (empty($getid3_temp->info['error'])) { + $info['riff'] = $getid3_temp->info['riff']; } else { - $ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']); + $info['warning'][] = 'Error parsing RIFF portion of La file: '.implode($getid3_temp->info['error']); } - unset($riff); - unset($dummy); - fclose($RIFF_fp); + unset($getid3_temp, $getid3_riff); } unlink($RIFFtempfilename); } } - // $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart']; - $ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset; + // $info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway + $info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart']; + $info['avdataoffset'] = $info['avdataoffset'] + $offset; - //$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']); - $ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']); - $ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels']; - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero'; + //$info['la']['codec'] = RIFFwFormatTagLookup($info['la']['raw']['format']); + $info['la']['compression_ratio'] = (float) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']); + $info['playtime_seconds'] = (float) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels']; + if ($info['playtime_seconds'] == 0) { + $info['error'][] = 'Corrupt LA file: playtime_seconds == zero'; return false; } - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; - //$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample']; + $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; + //$info['audio']['codec'] = $info['la']['codec']; + $info['audio']['bits_per_sample'] = $info['la']['bits_per_sample']; break; default: if (substr($rawdata, $offset, 2) == 'LA') { - $ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.'; + $info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] does not support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.'; } else { - $ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file'; + $info['error'][] = 'Not a LA (Lossless-Audio) file'; } return false; break; } - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels']; - $ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate']; - $ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version']; + $info['audio']['channels'] = $info['la']['channels']; + $info['audio']['sample_rate'] = (int) $info['la']['sample_rate']; + $info['audio']['encoder'] = 'LA v'.$info['la']['version']; return true; } diff --git a/modules/getid3/module.audio.lpac.php b/modules/getid3/module.audio.lpac.php index 3c044921..6ef0fb8a 100644 --- a/modules/getid3/module.audio.lpac.php +++ b/modules/getid3/module.audio.lpac.php @@ -15,107 +15,111 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -class getid3_lpac +class getid3_lpac extends getid3_handler { - function getid3_lpac(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $LPACheader = fread($fd, 14); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $LPACheader = fread($this->getid3->fp, 14); if (substr($LPACheader, 0, 4) != 'LPAC') { - $ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; + $info['error'][] = 'Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"'; return false; } - $ThisFileInfo['avdataoffset'] += 14; + $info['avdataoffset'] += 14; - $ThisFileInfo['fileformat'] = 'lpac'; - $ThisFileInfo['audio']['dataformat'] = 'lpac'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['fileformat'] = 'lpac'; + $info['audio']['dataformat'] = 'lpac'; + $info['audio']['lossless'] = true; + $info['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1)); + $info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1)); $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1)); - $ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4)); + $info['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4)); $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4)); - $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); - $ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04); - $ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02); - $ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01); + $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); + $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04); + $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02); + $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01); - if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) { - $ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set'; + if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) { + $info['warning'][] = '24-bit and 16-bit flags cannot both be set'; } - $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); - $ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000); - $ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; - $ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000); - $ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000); - $ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000); - $ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; - $ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F); - - if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) { - $ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"'; + $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); + $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000); + $info['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; + $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000); + $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000); + $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000); + $info['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; + $info['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F); + + if ($info['lpac']['flags']['fast_compress'] && ($info['lpac']['max_prediction_order'] != 3)) { + $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info['lpac']['max_prediction_order'].'"'; } - switch ($ThisFileInfo['lpac']['file_version']) { + switch ($info['lpac']['file_version']) { case 6: - if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) { - $ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; + if ($info['lpac']['flags']['adaptive_quantization']) { + $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; } - if ($ThisFileInfo['lpac']['quantization'] != 20) { - $ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q']; + if ($info['lpac']['quantization'] != 20) { + $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$info['lpac']['flags']['Q']; } break; default: - //$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org'; + //$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org'; break; } - $dummy = $ThisFileInfo; - $riff = new getid3_riff($fd, $dummy); - unset($riff); - $ThisFileInfo['avdataoffset'] = $dummy['avdataoffset']; - $ThisFileInfo['riff'] = $dummy['riff']; - $ThisFileInfo['error'] = $dummy['error']; - $ThisFileInfo['warning'] = $dummy['warning']; - $ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments']; - $ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate']; - - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1); - - if ($ThisFileInfo['lpac']['flags']['24_bit']) { - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; - } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) { - $ThisFileInfo['audio']['bits_per_sample'] = 16; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info = $info; + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->Analyze(); + $info['avdataoffset'] = $getid3_temp->info['avdataoffset']; + $info['riff'] = $getid3_temp->info['riff']; + $info['error'] = $getid3_temp->info['error']; + $info['warning'] = $getid3_temp->info['warning']; + $info['lpac']['comments']['comment'] = $getid3_temp->info['comments']; + $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate']; + unset($getid3_temp, $getid3_riff); + + $info['audio']['channels'] = ($info['lpac']['flags']['stereo'] ? 2 : 1); + + if ($info['lpac']['flags']['24_bit']) { + $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample']; + } elseif ($info['lpac']['flags']['16_bit']) { + $info['audio']['bits_per_sample'] = 16; } else { - $ThisFileInfo['audio']['bits_per_sample'] = 8; + $info['audio']['bits_per_sample'] = 8; } - if ($ThisFileInfo['lpac']['flags']['fast_compress']) { + if ($info['lpac']['flags']['fast_compress']) { // fast - $ThisFileInfo['audio']['encoder_options'] = '-1'; + $info['audio']['encoder_options'] = '-1'; } else { - switch ($ThisFileInfo['lpac']['max_prediction_order']) { + switch ($info['lpac']['max_prediction_order']) { case 20: // simple - $ThisFileInfo['audio']['encoder_options'] = '-2'; + $info['audio']['encoder_options'] = '-2'; break; case 30: // medium - $ThisFileInfo['audio']['encoder_options'] = '-3'; + $info['audio']['encoder_options'] = '-3'; break; case 40: // high - $ThisFileInfo['audio']['encoder_options'] = '-4'; + $info['audio']['encoder_options'] = '-4'; break; case 60: // extrahigh - $ThisFileInfo['audio']['encoder_options'] = '-5'; + $info['audio']['encoder_options'] = '-5'; break; } } - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate']; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; return true; } diff --git a/modules/getid3/module.audio.midi.php b/modules/getid3/module.audio.midi.php index a36a9f8d..7b839cf1 100644 --- a/modules/getid3/module.audio.midi.php +++ b/modules/getid3/module.audio.midi.php @@ -13,27 +13,31 @@ // /// ///////////////////////////////////////////////////////////////// +define('GETID3_MIDI_MAGIC_MTHD', 'MThd'); // MIDI file header magic +define('GETID3_MIDI_MAGIC_MTRK', 'MTrk'); // MIDI track header magic -class getid3_midi +class getid3_midi extends getid3_handler { + var $scanwholefile = true; - function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) { + function Analyze() { + $info = &$this->getid3->info; // shortcut - $ThisFileInfo['midi']['raw'] = array(); - $thisfile_midi = &$ThisFileInfo['midi']; + $info['midi']['raw'] = array(); + $thisfile_midi = &$info['midi']; $thisfile_midi_raw = &$thisfile_midi['raw']; - $ThisFileInfo['fileformat'] = 'midi'; - $ThisFileInfo['audio']['dataformat'] = 'midi'; + $info['fileformat'] = 'midi'; + $info['audio']['dataformat'] = 'midi'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $MIDIdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $offset = 0; $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' - if ($MIDIheaderID != 'MThd') { - $ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"'; - unset($ThisFileInfo['fileformat']); + if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTHD).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($MIDIheaderID).'"'; + unset($info['fileformat']); return false; } $offset += 4; @@ -47,33 +51,33 @@ class getid3_midi $offset += 2; for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) { - if ((strlen($MIDIdata) - $offset) < 8) { - $MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); + while ((strlen($MIDIdata) - $offset) < 8) { + $MIDIdata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size()); } $trackID = substr($MIDIdata, $offset, 4); $offset += 4; - if ($trackID == 'MTrk') { + if ($trackID == GETID3_MIDI_MAGIC_MTRK) { $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); $offset += 4; // $thisfile_midi['tracks'][$i]['size'] = $tracksize; $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize); $offset += $tracksize; } else { - $ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead'; + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTRK).'" at '.($offset - 4).', found "'.getid3_lib::PrintHexBytes($trackID).'" instead'; return false; } } if (!isset($trackdataarray) || !is_array($trackdataarray)) { - $ThisFileInfo['error'][] = 'Cannot find MIDI track information'; + $info['error'][] = 'Cannot find MIDI track information'; unset($thisfile_midi); - unset($ThisFileInfo['fileformat']); + unset($info['fileformat']); return false; } - if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important + if ($this->scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important $thisfile_midi['totalticks'] = 0; - $ThisFileInfo['playtime_seconds'] = 0; + $info['playtime_seconds'] = 0; $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 (); @@ -215,7 +219,7 @@ class getid3_midi case 0x51: // Tempo: microseconds / quarter note $CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); if ($CurrentMicroSecondsPerBeat == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero'; + $info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero'; return false; } $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat; @@ -258,13 +262,13 @@ class getid3_midi break; default: - $ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand; + $info['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand; break; } } else { - $ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel']; + $info['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel']; } } @@ -284,11 +288,11 @@ class getid3_midi if ($thisfile_midi['totalticks'] > $tickoffset) { if ($thisfile_midi_raw['ticksperqnote'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; + $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; return false; } - $ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000); + $info['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000); $prevmicrosecondsperbeat = $microsecondsperbeat; $previoustickoffset = $tickoffset; @@ -297,18 +301,18 @@ class getid3_midi if ($thisfile_midi['totalticks'] > $previoustickoffset) { if ($thisfile_midi_raw['ticksperqnote'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; + $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; return false; } - $ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000); + $info['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000); } } - if (!empty($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + if (!empty($info['playtime_seconds'])) { + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } if (!empty($thisfile_midi['lyrics'])) { diff --git a/modules/getid3/module.audio.mod.php b/modules/getid3/module.audio.mod.php index d5d3ea12..b8817694 100644 --- a/modules/getid3/module.audio.mod.php +++ b/modules/getid3/module.audio.mod.php @@ -14,84 +14,84 @@ ///////////////////////////////////////////////////////////////// -class getid3_mod +class getid3_mod extends getid3_handler { - // new combined constructor - function getid3_mod(&$fd, &$ThisFileInfo, $option) { - - if ($option === 'mod') { - $this->getMODheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 'xm') { - $this->getXMheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 'it') { - $this->getITheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 's3m') { - $this->getS3MheaderFilepointer($fd, $ThisFileInfo); + function Analyze() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $fileheader = fread($this->getid3->fp, 1088); + if (preg_match('#^IMPM#', $fileheader)) { + return $this->getITheaderFilepointer(); + } elseif (preg_match('#^Extended Module#', $fileheader)) { + return $this->getXMheaderFilepointer(); + } elseif (preg_match('#^.{44}SCRM#', $fileheader)) { + return $this->getS3MheaderFilepointer(); + } elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#', $fileheader)) { + return $this->getMODheaderFilepointer(); } + $info['error'][] = 'This is not a known type of MOD file'; + return false; } - function getMODheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'] + 1080); - $FormatID = fread($fd, 4); + function getMODheaderFilepointer() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'] + 1080); + $FormatID = fread($this->getid3->fp, 4); if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a known type of MOD file'; + $info['error'][] = 'This is not a known type of MOD file'; return false; } - $ThisFileInfo['fileformat'] = 'mod'; + $info['fileformat'] = 'mod'; - $ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()'; + $info['error'][] = 'MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } - function getXMheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset']); - $FormatID = fread($fd, 15); + function getXMheaderFilepointer() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset']); + $FormatID = fread($this->getid3->fp, 15); if (!preg_match('#^Extended Module$#', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file'; + $info['error'][] = 'This is not a known type of XM-MOD file'; return false; } - $ThisFileInfo['fileformat'] = 'xm'; + $info['fileformat'] = 'xm'; - $ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()'; + $info['error'][] = 'XM-MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } - function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'] + 44); - $FormatID = fread($fd, 4); + function getS3MheaderFilepointer() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'] + 44); + $FormatID = fread($this->getid3->fp, 4); if (!preg_match('#^SCRM$#', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file'; + $info['error'][] = 'This is not a ScreamTracker MOD file'; return false; } - $ThisFileInfo['fileformat'] = 's3m'; + $info['fileformat'] = 's3m'; - $ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()'; + $info['error'][] = 'ScreamTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } - function getITheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset']); - $FormatID = fread($fd, 4); + function getITheaderFilepointer() { + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset']); + $FormatID = fread($this->getid3->fp, 4); if (!preg_match('#^IMPM$#', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file'; + $info['error'][] = 'This is not an ImpulseTracker MOD file'; return false; } - $ThisFileInfo['fileformat'] = 'it'; + $info['fileformat'] = 'it'; - $ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()'; + $info['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } diff --git a/modules/getid3/module.audio.monkey.php b/modules/getid3/module.audio.monkey.php index 42382ad1..ffaeae9f 100644 --- a/modules/getid3/module.audio.monkey.php +++ b/modules/getid3/module.audio.monkey.php @@ -14,29 +14,32 @@ ///////////////////////////////////////////////////////////////// -class getid3_monkey +class getid3_monkey extends getid3_handler { - function getid3_monkey(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; + // based loosely on code from TMonkey by Jurgen Faul <jfaulŘgmx*de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - $ThisFileInfo['fileformat'] = 'mac'; - $ThisFileInfo['audio']['dataformat'] = 'mac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; + $info['fileformat'] = 'mac'; + $info['audio']['dataformat'] = 'mac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; - $ThisFileInfo['monkeys_audio']['raw'] = array(); - $thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio']; + $info['monkeys_audio']['raw'] = array(); + $thisfile_monkeysaudio = &$info['monkeys_audio']; $thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MACheaderData = fread($fd, 74); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $MACheaderData = fread($this->getid3->fp, 74); $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4); - if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') { - $ThisFileInfo['error'][] = 'Expecting "MAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_monkeysaudio_raw['magic'].'"'; - unset($ThisFileInfo['fileformat']); + $magic = 'MAC '; + if ($thisfile_monkeysaudio_raw['magic'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_monkeysaudio_raw['magic']).'"'; + unset($info['fileformat']); return false; } $thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+ @@ -105,13 +108,13 @@ class getid3_monkey } $thisfile_monkeysaudio['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16)); $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels']; - $ThisFileInfo['audio']['channels'] = $thisfile_monkeysaudio['channels']; + $info['audio']['channels'] = $thisfile_monkeysaudio['channels']; $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate']; if ($thisfile_monkeysaudio['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero'; + $info['error'][] = 'Corrupt MAC file: frequency == zero'; return false; } - $ThisFileInfo['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate']; + $info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate']; if ($thisfile_monkeysaudio['flags']['peak_level']) { $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel']; $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1); @@ -123,52 +126,52 @@ class getid3_monkey } $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate']; if ($thisfile_monkeysaudio['playtime'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero'; + $info['error'][] = 'Corrupt MAC file: playtime == zero'; return false; } - $ThisFileInfo['playtime_seconds'] = $thisfile_monkeysaudio['playtime']; - $thisfile_monkeysaudio['compressed_size'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; + $info['playtime_seconds'] = $thisfile_monkeysaudio['playtime']; + $thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset']; $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8); if ($thisfile_monkeysaudio['uncompressed_size'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: uncompressed_size == zero'; + $info['error'][] = 'Corrupt MAC file: uncompressed_size == zero'; return false; } $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']); $thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio']; - $ThisFileInfo['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate']; + $info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate']; // add size of MAC header to avdataoffset if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes']; + $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes']; + $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes']; + $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes']; + $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes']; - $ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes']; + $info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes']; } else { - $ThisFileInfo['avdataoffset'] += $offset; + $info['avdataoffset'] += $offset; } if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) { - //$ThisFileInfo['warning'][] = 'cFileMD5 is null'; + //$info['warning'][] = 'cFileMD5 is null'; } else { - $ThisFileInfo['md5_data_source'] = ''; + $info['md5_data_source'] = ''; $md5 = $thisfile_monkeysaudio_raw['cFileMD5']; for ($i = 0; $i < strlen($md5); $i++) { - $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); + $info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); } - if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { - unset($ThisFileInfo['md5_data_source']); + if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) { + unset($info['md5_data_source']); } } } - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample']; - $ThisFileInfo['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2); - $ThisFileInfo['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression'; + $info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample']; + $info['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2); + $info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression'; return true; } diff --git a/modules/getid3/module.audio.mp3.php b/modules/getid3/module.audio.mp3.php index b97a7041..909646e1 100644 --- a/modules/getid3/module.audio.mp3.php +++ b/modules/getid3/module.audio.mp3.php @@ -21,65 +21,70 @@ define('GETID3_MP3_VALID_CHECK_FRAMES', 35); -class getid3_mp3 +class getid3_mp3 extends getid3_handler { var $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 - function getid3_mp3(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) { + $initialOffset = $info['avdataoffset']; + + if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) { if ($this->allow_bruteforce) { - $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode'; - $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo); + $info['error'][] = 'Rescanning file in BruteForce mode'; + $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info); } } - if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) { - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + if (isset($info['mpeg']['audio']['bitrate_mode'])) { + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); } - if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) { + if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) { $synchoffsetwarning = 'Unknown data before synch '; - if (isset($ThisFileInfo['id3v2']['headerlength'])) { - $synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, '; + if (isset($info['id3v2']['headerlength'])) { + $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, '; + } elseif ($initialOffset > 0) { + $synchoffsetwarning .= '(should be at '.$initialOffset.', '; } else { $synchoffsetwarning .= '(should be at beginning of file, '; } - $synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')'; - if (isset($ThisFileInfo['audio']['bitrate_mode']) && ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr')) { + $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')'; + if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) { - if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) { + if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) { $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; - $ThisFileInfo['audio']['codec'] = 'LAME'; + $info['audio']['codec'] = 'LAME'; $CurrentDataLAMEversionString = 'LAME3.'; - } elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) { + } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) { $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; - $ThisFileInfo['audio']['codec'] = 'LAME'; + $info['audio']['codec'] = 'LAME'; $CurrentDataLAMEversionString = 'LAME3.'; } } - $ThisFileInfo['warning'][] = $synchoffsetwarning; + $info['warning'][] = $synchoffsetwarning; } - if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) { - $ThisFileInfo['audio']['codec'] = 'LAME'; - if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00"); - } elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00"); + if (isset($info['mpeg']['audio']['LAME'])) { + $info['audio']['codec'] = 'LAME'; + if (!empty($info['mpeg']['audio']['LAME']['long_version'])) { + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00"); + } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) { + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00"); } } - $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($ThisFileInfo['audio']['encoder']) ? $ThisFileInfo['audio']['encoder'] : '')); + $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : '')); if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) { // a version number of LAME that does not end with a number like "LAME3.92" // or with a closing parenthesis like "LAME3.88 (alpha)" @@ -89,9 +94,9 @@ class getid3_mp3 $PossiblyLongerLAMEversion_FrameLength = 1441; // Not sure what version of LAME this is - look in padding of last frame for longer version string - $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; - fseek($fd, $PossibleLAMEversionStringOffset); - $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength); + $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; + fseek($this->getid3->fp, $PossibleLAMEversionStringOffset); + $PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength); switch (substr($CurrentDataLAMEversionString, -1)) { case 'a': case 'b': @@ -103,62 +108,63 @@ class getid3_mp3 if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) { if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) { $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" - if (empty($ThisFileInfo['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($ThisFileInfo['audio']['encoder']))) { - $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; + if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) { + $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; } } } } - if (!empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 "); + if (!empty($info['audio']['encoder'])) { + $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 "); } - switch (isset($ThisFileInfo['mpeg']['audio']['layer']) ? $ThisFileInfo['mpeg']['audio']['layer'] : '') { + switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') { case 1: case 2: - $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; + $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer']; break; } - if (isset($ThisFileInfo['fileformat']) && ($ThisFileInfo['fileformat'] == 'mp3')) { - switch ($ThisFileInfo['audio']['dataformat']) { + if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) { + switch ($info['audio']['dataformat']) { case 'mp1': case 'mp2': case 'mp3': - $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + $info['fileformat'] = $info['audio']['dataformat']; break; default: - $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"'; + $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"'; break; } } - if (empty($ThisFileInfo['fileformat'])) { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']['bitrate_mode']); - unset($ThisFileInfo['avdataoffset']); - unset($ThisFileInfo['avdataend']); + if (empty($info['fileformat'])) { + unset($info['fileformat']); + unset($info['audio']['bitrate_mode']); + unset($info['avdataoffset']); + unset($info['avdataend']); return false; } - $ThisFileInfo['mime_type'] = 'audio/mpeg'; - $ThisFileInfo['audio']['lossless'] = false; + $info['mime_type'] = 'audio/mpeg'; + $info['audio']['lossless'] = false; // Calculate playtime - if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) { - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; + if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) { + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; } - $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo); + $info['audio']['encoder_options'] = $this->GuessEncoderOptions(); return true; } - static function GuessEncoderOptions(&$ThisFileInfo) { + function GuessEncoderOptions() { // shortcuts - if (!empty($ThisFileInfo['mpeg']['audio'])) { - $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + $info = &$this->getid3->info; + if (!empty($info['mpeg']['audio'])) { + $thisfile_mpeg_audio = &$info['mpeg']['audio']; if (!empty($thisfile_mpeg_audio['LAME'])) { $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; } @@ -242,7 +248,7 @@ class getid3_mp3 $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; - } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') { + } elseif ($info['audio']['bitrate_mode'] == 'vbr') { // http://gabriel.mp3-tech.org/mp3infotag.html // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h @@ -252,13 +258,13 @@ class getid3_mp3 $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10); $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value; - } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + } elseif ($info['audio']['bitrate_mode'] == 'cbr') { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); } else { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + $encoder_options = strtoupper($info['audio']['bitrate_mode']); } @@ -266,12 +272,12 @@ class getid3_mp3 $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; - } elseif (!empty($ThisFileInfo['audio']['bitrate'])) { + } elseif (!empty($info['audio']['bitrate'])) { - if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + if ($info['audio']['bitrate_mode'] == 'cbr') { + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); } else { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + $encoder_options = strtoupper($info['audio']['bitrate_mode']); } } @@ -389,17 +395,16 @@ class getid3_mp3 } } } - if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) { - //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) { + //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); + $encoder_options = strtoupper($info['audio']['bitrate_mode']); } return $encoder_options; } - static function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { - + function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; @@ -417,13 +422,12 @@ class getid3_mp3 $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); } - if ($offset >= $ThisFileInfo['avdataend']) { - $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch'; + if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) { + $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset; return false; } - fseek($fd, $offset, SEEK_SET); - //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame - $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data + //$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame + $headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data // MP3 audio frame structure: // $aa $aa $aa $aa [$bb $bb] $cc... @@ -442,29 +446,26 @@ class getid3_mp3 } static $MPEGaudioHeaderValidCache = array(); - - // Not in cache - if (!isset($MPEGaudioHeaderValidCache[$head4])) { + if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); } // shortcut - if (!isset($ThisFileInfo['mpeg']['audio'])) { - $ThisFileInfo['mpeg']['audio'] = array(); + if (!isset($info['mpeg']['audio'])) { + $info['mpeg']['audio'] = array(); } - $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + $thisfile_mpeg_audio = &$info['mpeg']['audio']; if ($MPEGaudioHeaderValidCache[$head4]) { $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; } else { - $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset; + $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset; return false; } if (!$FastMPEGheaderScan) { - $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']]; @@ -478,24 +479,23 @@ class getid3_mp3 $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']]; - $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; + $info['audio']['channels'] = $thisfile_mpeg_audio['channels']; + $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; if ($thisfile_mpeg_audio['protection']) { $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2)); } - } if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 - $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; + $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; $thisfile_mpeg_audio['raw']['bitrate'] = 0; } $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; - if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) { + if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) { // only skip multiple frame check if free-format bitstream found at beginning of file // otherwise is quite possibly simply corrupted data $recursivesearch = false; @@ -504,14 +504,14 @@ class getid3_mp3 // For Layer 2 there are some combinations of bitrate and mode which are not allowed. if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) { - $ThisFileInfo['audio']['dataformat'] = 'mp2'; + $info['audio']['dataformat'] = 'mp2'; switch ($thisfile_mpeg_audio['channelmode']) { case 'mono': if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { // these are ok } else { - $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; return false; } break; @@ -522,7 +522,7 @@ class getid3_mp3 if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { // these are ok } else { - $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; return false; } break; @@ -532,19 +532,19 @@ class getid3_mp3 } - if ($ThisFileInfo['audio']['sample_rate'] > 0) { - $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']); + if ($info['audio']['sample_rate'] > 0) { + $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']); } $nextframetestoffset = $offset + 1; if ($thisfile_mpeg_audio['bitrate'] != 'free') { - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; if (isset($thisfile_mpeg_audio['framelength'])) { $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength']; } else { - $ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; + $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; return false; } @@ -561,7 +561,7 @@ class getid3_mp3 $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; - $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; + $info['audio']['codec'] = 'Fraunhofer'; $SideInfoData = substr($headerstring, 4 + 2, 32); @@ -653,12 +653,12 @@ class getid3_mp3 if ($thisfile_mpeg_audio['layer'] == '1') { // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 - //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; - $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12; + //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12; } else { // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 - //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; - $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144; + //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144; } $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); } @@ -759,10 +759,10 @@ class getid3_mp3 $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']); if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { - $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; } - $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; - $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; + $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; + $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; } else { unset($thisfile_mpeg_audio_lame_RGAD['track']); } @@ -777,10 +777,10 @@ class getid3_mp3 $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']); if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { - $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; } - $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; + $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; + $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; } else { unset($thisfile_mpeg_audio_lame_RGAD['album']); } @@ -836,7 +836,7 @@ class getid3_mp3 $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { - $ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; + $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; } if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { // this may change if 3.90.4 ever comes out @@ -859,7 +859,7 @@ class getid3_mp3 $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; //} @@ -875,12 +875,12 @@ class getid3_mp3 $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; if ($recursivesearch) { $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; - if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { + if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) { $recursivesearch = false; $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; } if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { - $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; + $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; } } @@ -888,47 +888,47 @@ class getid3_mp3 } - if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) { - if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { - if (isset($ThisFileInfo['fileformat']) && ($ThisFileInfo['fileformat'] == 'riff')) { + if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) { + if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) { + if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) { // ignore, audio data is broken into chunks so will always be data "missing" - } elseif (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) { - $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; + } elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) { + $info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; } else { - $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)'; + $info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)'; } } else { - if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { - // $prenullbytefileoffset = ftell($fd); - // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); - // $PossibleNullByte = fread($fd, 1); - // fseek($fd, $prenullbytefileoffset, SEEK_SET); + if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { + // $prenullbytefileoffset = ftell($this->getid3->fp); + // fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); + // $PossibleNullByte = fread($this->getid3->fp, 1); + // fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET); // if ($PossibleNullByte === "\x00") { - $ThisFileInfo['avdataend']--; - // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + $info['avdataend']--; + // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; // } else { - // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; // } } else { - $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; } } } - if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) { - if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { - $framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); + if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) { + if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { + $framebytelength = $this->FreeFormatFrameLength($offset, true); if ($framebytelength > 0) { $thisfile_mpeg_audio['framelength'] = $framebytelength; if ($thisfile_mpeg_audio['layer'] == '1') { // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 - $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; } else { // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 - $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; } } else { - $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; + $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; } } } @@ -943,9 +943,9 @@ class getid3_mp3 } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { $bytes_per_frame = 576; } - $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / $bytes_per_frame) : 0); + $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0); if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; + $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion } break; @@ -957,7 +957,7 @@ class getid3_mp3 if ($recursivesearch) { - if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { + if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) { return false; } @@ -997,7 +997,7 @@ class getid3_mp3 // } // // if ($thisfile_mpeg_audio['version'] == '1') { - // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) { // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 2; @@ -1005,7 +1005,7 @@ class getid3_mp3 // } // } // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) { - // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) { // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); // $SideInfoOffset += 12; // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); @@ -1069,20 +1069,23 @@ class getid3_mp3 return true; } - static function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) { + function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { + $info = &$this->getid3->info; + $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false); + for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch - if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) { + if (($nextframetestoffset + 4) >= $info['avdataend']) { // end of file return true; } - $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); - if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) { + $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { if ($ScanAsCBR) { - // force CBR mode, used for trying to pick out invalid audio streams with - // valid(?) VBR headers, or VBR streams with no VBR header - if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) { + // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header + if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) { return false; } } @@ -1092,14 +1095,19 @@ class getid3_mp3 if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; } else { - $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; + $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; return false; } + } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) { + + // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK + return true; + } else { // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence - $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; + $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; return false; } @@ -1107,9 +1115,11 @@ class getid3_mp3 return true; } - static function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) { - fseek($fd, $offset, SEEK_SET); - $MPEGaudioData = fread($fd, 32768); + function FreeFormatFrameLength($offset, $deepscan=false) { + $info = &$this->getid3->info; + + fseek($this->getid3->fp, $offset, SEEK_SET); + $MPEGaudioData = fread($this->getid3->fp, 32768); $SyncPattern1 = substr($MPEGaudioData, 0, 4); // may be different pattern due to padding @@ -1140,12 +1150,12 @@ class getid3_mp3 $framelength = $framelength2; } if (!$framelength) { - $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; + $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; return false; } else { - $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; - $ThisFileInfo['audio']['codec'] = 'LAME'; - $ThisFileInfo['audio']['encoder'] = 'LAME3.88'; + $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; + $info['audio']['codec'] = 'LAME'; + $info['audio']['encoder'] = 'LAME3.88'; $SyncPattern1 = substr($SyncPattern1, 0, 3); $SyncPattern2 = substr($SyncPattern2, 0, 3); } @@ -1155,9 +1165,9 @@ class getid3_mp3 $ActualFrameLengthValues = array(); $nextoffset = $offset + $framelength; - while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) { - fseek($fd, $nextoffset - 1, SEEK_SET); - $NextSyncPattern = fread($fd, 6); + while ($nextoffset < ($info['avdataend'] - 6)) { + fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET); + $NextSyncPattern = fread($this->getid3->fp, 6); if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { // good - found where expected $ActualFrameLengthValues[] = $framelength; @@ -1170,7 +1180,7 @@ class getid3_mp3 $ActualFrameLengthValues[] = ($framelength + 1); $nextoffset++; } else { - $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; + $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; return false; } $nextoffset += $framelength; @@ -1182,11 +1192,10 @@ class getid3_mp3 return $framelength; } - function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) { - - $MPEGaudioHeaderDecodeCache = array(); - $MPEGaudioHeaderValidCache = array(); - $MPEGaudioHeaderLengthCache = array(); + function getOnlyMPEGaudioInfoBruteForce() { + $MPEGaudioHeaderDecodeCache = array(); + $MPEGaudioHeaderValidCache = array(); + $MPEGaudioHeaderLengthCache = array(); $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); @@ -1194,34 +1203,34 @@ class getid3_mp3 $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); - $LongMPEGversionLookup = array(); - $LongMPEGlayerLookup = array(); - $LongMPEGbitrateLookup = array(); - $LongMPEGpaddingLookup = array(); - $LongMPEGfrequencyLookup = array(); - - $Distribution['bitrate'] = array(); - $Distribution['frequency'] = array(); - $Distribution['layer'] = array(); - $Distribution['version'] = array(); - $Distribution['padding'] = array(); - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $LongMPEGversionLookup = array(); + $LongMPEGlayerLookup = array(); + $LongMPEGbitrateLookup = array(); + $LongMPEGpaddingLookup = array(); + $LongMPEGfrequencyLookup = array(); + $Distribution['bitrate'] = array(); + $Distribution['frequency'] = array(); + $Distribution['layer'] = array(); + $Distribution['version'] = array(); + $Distribution['padding'] = array(); + + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $max_frames_scan = 5000; $frames_scanned = 0; - $previousvalidframe = $ThisFileInfo['avdataoffset']; - while (ftell($fd) < $ThisFileInfo['avdataend']) { + $previousvalidframe = $info['avdataoffset']; + while (ftell($this->getid3->fp) < $info['avdataend']) { set_time_limit(30); - $head4 = fread($fd, 4); + $head4 = fread($this->getid3->fp, 4); if (strlen($head4) < 4) { break; } if ($head4{0} != "\xFF") { for ($i = 1; $i < 4; $i++) { if ($head4{$i} == "\xFF") { - fseek($fd, $i - 4, SEEK_CUR); + fseek($this->getid3->fp, $i - 4, SEEK_CUR); continue 2; } } @@ -1249,9 +1258,9 @@ class getid3_mp3 $LongMPEGfrequencyLookup[$head4]); } if ($MPEGaudioHeaderLengthCache[$head4] > 4) { - $WhereWeWere = ftell($fd); - fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); - $next4 = fread($fd, 4); + $WhereWeWere = ftell($this->getid3->fp); + fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); + $next4 = fread($this->getid3->fp, 4); if ($next4{0} == "\xFF") { if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4); @@ -1260,7 +1269,7 @@ class getid3_mp3 $MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); } if ($MPEGaudioHeaderValidCache[$next4]) { - fseek($fd, -4, SEEK_CUR); + fseek($this->getid3->fp, -4, SEEK_CUR); getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]); getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]); @@ -1268,8 +1277,8 @@ class getid3_mp3 getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { - $pct_data_scanned = (ftell($fd) - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); - $ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; + $pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); + $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; foreach ($Distribution as $key1 => $value1) { foreach ($value1 as $key2 => $value2) { $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned); @@ -1281,7 +1290,7 @@ class getid3_mp3 } } unset($next4); - fseek($fd, $WhereWeWere - 3, SEEK_SET); + fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET); } } @@ -1290,19 +1299,19 @@ class getid3_mp3 ksort($Distribution[$key], SORT_NUMERIC); } ksort($Distribution['version'], SORT_STRING); - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; - $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; - $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; - $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version']; - $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; + $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; + $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; + $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; + $info['mpeg']['audio']['version_distribution'] = $Distribution['version']; + $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; if (count($Distribution['version']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected'; + $info['error'][] = 'Corrupt file - more than one MPEG version detected'; } if (count($Distribution['layer']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected'; + $info['error'][] = 'Corrupt file - more than one MPEG layer detected'; } if (count($Distribution['frequency']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; + $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; } @@ -1312,29 +1321,30 @@ class getid3_mp3 $bittotal += ($bitratevalue * $bitratecount); } } - $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); - if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) { - $ThisFileInfo['error'][] = 'no MPEG audio frames found'; + $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); + if ($info['mpeg']['audio']['frame_count'] == 0) { + $info['error'][] = 'no MPEG audio frames found'; return false; } - $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']); - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); - $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); + $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']); + $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); + $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); - $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); + $info['fileformat'] = $info['audio']['dataformat']; return true; } - static function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) { - + function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) { // looks for synch, decodes MPEG audio header + $info = &$this->getid3->info; + static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; @@ -1345,131 +1355,125 @@ class getid3_mp3 } - fseek($fd, $avdataoffset, SEEK_SET); - $sync_seek_buffer_size = min(128 * 1024, $ThisFileInfo['avdataend'] - $avdataoffset); + fseek($this->getid3->fp, $avdataoffset, SEEK_SET); + $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); if ($sync_seek_buffer_size <= 0) { - $ThisFileInfo['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; + $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; return false; } - $header = fread($fd, $sync_seek_buffer_size); + $header = fread($this->getid3->fp, $sync_seek_buffer_size); $sync_seek_buffer_size = strlen($header); $SynchSeekOffset = 0; while ($SynchSeekOffset < $sync_seek_buffer_size) { - - if ((($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) { + if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) { if ($SynchSeekOffset > $sync_seek_buffer_size) { // if a synch's not found within the first 128k bytes, then give up - $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); + $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); } - if (empty($ThisFileInfo['mpeg'])) { - unset($ThisFileInfo['mpeg']); + if (empty($info['mpeg'])) { + unset($info['mpeg']); } return false; - } elseif (feof($fd)) { + } elseif (feof($this->getid3->fp)) { - $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch before end of file'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); + $info['error'][] = 'Could not find valid MPEG audio synch before end of file'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); } - if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { - unset($ThisFileInfo['mpeg']); + if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) { + unset($info['mpeg']); } return false; } } if (($SynchSeekOffset + 1) >= strlen($header)) { - $ThisFileInfo['error'][] = 'Could not find valid MPEG synch before end of file'; + $info['error'][] = 'Could not find valid MPEG synch before end of file'; return false; } if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected - - if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { - $FirstFrameThisfileInfo = $ThisFileInfo; + if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) { + $FirstFrameThisfileInfo = $info; $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; - if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { + if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) { // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below unset($FirstFrameThisfileInfo); } } - $dummy = $ThisFileInfo; // only overwrite real data if valid header found - if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { - $ThisFileInfo = $dummy; - $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; - switch (isset($ThisFileInfo['fileformat']) ? $ThisFileInfo['fileformat'] : '') { + $dummy = $info; // only overwrite real data if valid header found + if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) { + $info = $dummy; + $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset; + switch (isset($info['fileformat']) ? $info['fileformat'] : '') { case '': case 'id3': case 'ape': case 'mp3': - $ThisFileInfo['fileformat'] = 'mp3'; - $ThisFileInfo['audio']['dataformat'] = 'mp3'; + $info['fileformat'] = 'mp3'; + $info['audio']['dataformat'] = 'mp3'; break; } if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { - if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { + 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. - $ThisFileInfo = $FirstFrameThisfileInfo; - $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; - $ThisFileInfo['fileformat'] = 'mp3'; - $ThisFileInfo['audio']['dataformat'] = 'mp3'; - $dummy = $ThisFileInfo; + $info = $FirstFrameThisfileInfo; + $info['avdataoffset'] = $FirstFrameAVDataOffset; + $info['fileformat'] = 'mp3'; + $info['audio']['dataformat'] = 'mp3'; + $dummy = $info; unset($dummy['mpeg']['audio']); $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; - if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { - - $ThisFileInfo = $dummy; - $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; - $ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; - + if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) { + $info = $dummy; + $info['avdataoffset'] = $GarbageOffsetEnd; + $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; } else { - - $ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; - + $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; } } } - if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { + if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { // VBR file with no VBR header $BitrateHistogram = true; } if ($BitrateHistogram) { - $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); - $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); + $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); + $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); - if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { - if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); + if ($info['mpeg']['audio']['version'] == '1') { + if ($info['mpeg']['audio']['layer'] == 3) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); + } elseif ($info['mpeg']['audio']['layer'] == 2) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); + } elseif ($info['mpeg']['audio']['layer'] == 1) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); } - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); + } elseif ($info['mpeg']['audio']['layer'] == 1) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); } else { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); } - $dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); - $synchstartoffset = $ThisFileInfo['avdataoffset']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + $synchstartoffset = $info['avdataoffset']; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); // you can play with these numbers: $max_frames_scan = 50000; @@ -1484,18 +1488,18 @@ class getid3_mp3 $pct_data_scanned = 0; for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { $frames_scanned_this_segment = 0; - if (ftell($fd) >= $ThisFileInfo['avdataend']) { + if (ftell($this->getid3->fp) >= $info['avdataend']) { break; } - $scan_start_offset[$current_segment] = max(ftell($fd), $ThisFileInfo['avdataoffset'] + round($current_segment * (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $max_scan_segments))); + $scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments))); if ($current_segment > 0) { - fseek($fd, $scan_start_offset[$current_segment], SEEK_SET); - $buffer_4k = fread($fd, 4096); + fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET); + $buffer_4k = fread($this->getid3->fp, 4096); for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) { if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected - if (getid3_mp3::decodeMPEGaudioHeader($fd, $scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { + if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength']; - if (getid3_mp3::decodeMPEGaudioHeader($fd, $calculated_next_offset, $dummy, false, false, $FastMode)) { + if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) { $scan_start_offset[$current_segment] += $j; break; } @@ -1504,7 +1508,7 @@ class getid3_mp3 } } $synchstartoffset = $scan_start_offset[$current_segment]; - while (getid3_mp3::decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) { + while ($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']]; @@ -1512,14 +1516,14 @@ class getid3_mp3 $SynchErrorsFound++; $synchstartoffset++; } else { - getid3_lib::safe_inc($ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]); - getid3_lib::safe_inc($ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]); - getid3_lib::safe_inc($ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]); + getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]); + getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]); + getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]); $synchstartoffset += $dummy['mpeg']['audio']['framelength']; } $frames_scanned++; if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) { - $this_pct_scanned = (ftell($fd) - $scan_start_offset[$current_segment]) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + $this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']); if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) { // file likely contains < $max_frames_scan, just scan as one segment $max_scan_segments = 1; @@ -1532,53 +1536,53 @@ class getid3_mp3 } } if ($pct_data_scanned > 0) { - $ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; - foreach ($ThisFileInfo['mpeg']['audio'] as $key1 => $value1) { + $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; + foreach ($info['mpeg']['audio'] as $key1 => $value1) { if (!preg_match('#_distribution$#i', $key1)) { continue; } foreach ($value1 as $key2 => $value2) { - $ThisFileInfo['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned); + $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned); } } } if ($SynchErrorsFound > 0) { - $ThisFileInfo['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis'; + $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis'; //return false; } $bittotal = 0; $framecounter = 0; - foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { $framecounter += $bitratecount; if ($bitratevalue != 'free') { $bittotal += ($bitratevalue * $bitratecount); } } if ($framecounter == 0) { - $ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero'; + $info['error'][] = 'Corrupt MP3 file: framecounter == zero'; return false; } - $ThisFileInfo['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); - $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); + $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); + $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently $distinct_bitrates = 0; - foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { if ($bitrate_count > 0) { $distinct_bitrates++; } } if ($distinct_bitrates > 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; + $info['mpeg']['audio']['bitrate_mode'] = 'vbr'; } else { - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; + $info['mpeg']['audio']['bitrate_mode'] = 'cbr'; } - $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; + $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; } @@ -1587,20 +1591,20 @@ class getid3_mp3 } $SynchSeekOffset++; - if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) { + if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) { // end of file/data - if (empty($ThisFileInfo['mpeg']['audio'])) { + if (empty($info['mpeg']['audio'])) { - $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); + $info['error'][] = 'could not find valid MPEG synch before end of file'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); } - if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) { - unset($ThisFileInfo['mpeg']); + if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) { + unset($info['mpeg']); } return false; @@ -1609,9 +1613,9 @@ class getid3_mp3 } } - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; + $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode']; + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; return true; } diff --git a/modules/getid3/module.audio.mpc.php b/modules/getid3/module.audio.mpc.php index 41f039cd..9a0b16d9 100644 --- a/modules/getid3/module.audio.mpc.php +++ b/modules/getid3/module.audio.mpc.php @@ -14,42 +14,44 @@ ///////////////////////////////////////////////////////////////// -class getid3_mpc +class getid3_mpc extends getid3_handler { - function getid3_mpc(&$fd, &$ThisFileInfo) { - $ThisFileInfo['mpc']['header'] = array(); - $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'mpc'; - $ThisFileInfo['audio']['dataformat'] = 'mpc'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only - $ThisFileInfo['audio']['lossless'] = false; + $info['mpc']['header'] = array(); + $thisfile_mpc_header = &$info['mpc']['header']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MPCheaderData = fread($fd, 4); - $ThisFileInfo['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6) - if (preg_match('#^MPCK#', $ThisFileInfo['mpc']['header']['preamble'])) { + $info['fileformat'] = 'mpc'; + $info['audio']['dataformat'] = 'mpc'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only + $info['audio']['lossless'] = false; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $MPCheaderData = fread($this->getid3->fp, 4); + $info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6) + if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) { // this is SV8 - return $this->ParseMPCsv8($fd, $ThisFileInfo); + return $this->ParseMPCsv8(); - } elseif (preg_match('#^MP\+#', $ThisFileInfo['mpc']['header']['preamble'])) { + } elseif (preg_match('#^MP\+#', $info['mpc']['header']['preamble'])) { // this is SV7 - return $this->ParseMPCsv7($fd, $ThisFileInfo); + return $this->ParseMPCsv7(); } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) { // this is SV4 - SV6, handle seperately - return $this->ParseMPCsv6($fd, $ThisFileInfo); + return $this->ParseMPCsv6(); } else { - $ThisFileInfo['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, 0, 4).'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['mpc']); + $info['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($MPCheaderData, 0, 4)).'"'; + unset($info['fileformat']); + unset($info['mpc']); return false; } @@ -57,35 +59,36 @@ class getid3_mpc } - function ParseMPCsv8(&$fd, &$ThisFileInfo) { + function ParseMPCsv8() { // this is SV8 // http://trac.musepack.net/trac/wiki/SV8Specification - $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; + $info = &$this->getid3->info; + $thisfile_mpc_header = &$info['mpc']['header']; $keyNameSize = 2; $maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10" - $offset = ftell($fd); - while ($offset < $ThisFileInfo['avdataend']) { + $offset = ftell($this->getid3->fp); + while ($offset < $info['avdataend']) { $thisPacket = array(); $thisPacket['offset'] = $offset; $packet_offset = 0; // Size is a variable-size field, could be 1-4 bytes (possibly more?) // read enough data in and figure out the exact size later - $MPCheaderData = fread($fd, $keyNameSize + $maxHandledPacketLength); + $MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength); $packet_offset += $keyNameSize; $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize); $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']); if ($thisPacket['key'] == $thisPacket['key_name']) { - $ThisFileInfo['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']; + $info['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']; return false; } $packetLength = 0; $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field if ($thisPacket['packet_size'] === false) { - $ThisFileInfo['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize); + $info['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize); return false; } $packet_offset += $packetLength; @@ -95,7 +98,7 @@ class getid3_mpc case 'SH': // Stream Header $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { - $MPCheaderData .= fread($fd, $moreBytesToRead); + $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); } $thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4)); $packet_offset += 4; @@ -124,16 +127,16 @@ class getid3_mpc $thisfile_mpc_header['samples'] = $thisPacket['sample_count']; $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version']; - $ThisFileInfo['audio']['channels'] = $thisPacket['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisPacket['sample_frequency']; - $ThisFileInfo['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['audio']['channels'] = $thisPacket['channels']; + $info['audio']['sample_rate'] = $thisPacket['sample_frequency']; + $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency']; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; break; case 'RG': // Replay Gain $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { - $MPCheaderData .= fread($fd, $moreBytesToRead); + $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); } $thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; @@ -146,16 +149,16 @@ class getid3_mpc $thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; - if ($thisPacket['replaygain_title_gain']) { $ThisFileInfo['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; } - if ($thisPacket['replaygain_title_peak']) { $ThisFileInfo['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; } - if ($thisPacket['replaygain_album_gain']) { $ThisFileInfo['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; } - if ($thisPacket['replaygain_album_peak']) { $ThisFileInfo['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; } + if ($thisPacket['replaygain_title_gain']) { $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; } + if ($thisPacket['replaygain_title_peak']) { $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; } + if ($thisPacket['replaygain_album_gain']) { $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; } + if ($thisPacket['replaygain_album_peak']) { $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; } break; case 'EI': // Encoder Info $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { - $MPCheaderData .= fread($fd, $moreBytesToRead); + $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); } $profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; @@ -171,8 +174,8 @@ class getid3_mpc $packet_offset += 1; $thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build']; - $ThisFileInfo['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')'; - $thisfile_mpc_header['encoder_version'] = $ThisFileInfo['audio']['encoder']; + $info['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')'; + $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder']; //$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0 $thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0 break; @@ -191,28 +194,30 @@ class getid3_mpc break; default: - $ThisFileInfo['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']; + $info['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']; return false; break; } if (!empty($thisPacket)) { - $ThisFileInfo['mpc']['packets'][] = $thisPacket; + $info['mpc']['packets'][] = $thisPacket; } - fseek($fd, $offset); + fseek($this->getid3->fp, $offset); } $thisfile_mpc_header['size'] = $offset; return true; } - function ParseMPCsv7(&$fd, &$ThisFileInfo) { + function ParseMPCsv7() { // this is SV7 // http://www.uni-jena.de/~pfk/mpp/sv8/header.html - $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; + + $info = &$this->getid3->info; + $thisfile_mpc_header = &$info['mpc']['header']; $offset = 0; $thisfile_mpc_header['size'] = 28; - $MPCheaderData = $ThisFileInfo['mpc']['header']['preamble']; - $MPCheaderData .= fread($fd, $thisfile_mpc_header['size'] - strlen($ThisFileInfo['mpc']['header']['preamble'])); + $MPCheaderData = $info['mpc']['header']['preamble']; + $MPCheaderData .= fread($this->getid3->fp, $thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble'])); $offset = strlen('MP+'); $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); @@ -223,7 +228,7 @@ class getid3_mpc $offset += 4; if ($thisfile_mpc_header['stream_version_major'] != 7) { - $ThisFileInfo['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')'; + $info['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')'; return false; } @@ -262,22 +267,22 @@ class getid3_mpc $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']); $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']); if ($thisfile_mpc_header['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero'; + $info['error'][] = 'Corrupt MPC file: frequency == zero'; return false; } - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; - $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels']; + $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; + $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $info['audio']['channels']; - $ThisFileInfo['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate']; - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero'; + $info['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $info['audio']['channels']) / $info['audio']['sample_rate']; + if ($info['playtime_seconds'] == 0) { + $info['error'][] = 'Corrupt MPC file: playtime_seconds == zero'; return false; } // add size of file header to avdataoffset - calc bitrate correctly + MD5 data - $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; + $info['avdataoffset'] += $thisfile_mpc_header['size']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak']; $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']); @@ -296,37 +301,39 @@ class getid3_mpc } $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']); - $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db']; + $info['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db']; + $info['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db']; if ($thisfile_mpc_header['title_peak'] > 0) { - $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak']; + $info['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak']; } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) { - $ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c + $info['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c } if ($thisfile_mpc_header['album_peak'] > 0) { - $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak']; + $info['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak']; } - //$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version']; - $ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version']; - $ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile']; + //$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version']; + $info['audio']['encoder'] = $thisfile_mpc_header['encoder_version']; + $info['audio']['encoder_options'] = $thisfile_mpc_header['profile']; $thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0 return true; } - function ParseMPCsv6(&$fd, &$ThisFileInfo) { + function ParseMPCsv6() { // this is SV4 - SV6 - $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; + + $info = &$this->getid3->info; + $thisfile_mpc_header = &$info['mpc']['header']; $offset = 0; $thisfile_mpc_header['size'] = 8; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MPCheaderData = fread($fd, $thisfile_mpc_header['size']); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $MPCheaderData = fread($this->getid3->fp, $thisfile_mpc_header['size']); // add size of file header to avdataoffset - calc bitrate correctly + MD5 data - $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; + $info['avdataoffset'] += $thisfile_mpc_header['size']; // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :) $HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4)); @@ -362,29 +369,29 @@ class getid3_mpc break; default: - $ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead'; - unset($ThisFileInfo['mpc']); + $info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead'; + unset($info['mpc']); return false; break; } if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) { - $ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size']; + $info['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size']; } $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7 - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; - $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels']; + $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; + $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels']; if ($thisfile_mpc_header['target_bitrate'] == 0) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['bitrate_mode'] = 'vbr'; } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['bitrate_mode'] = 'cbr'; } - $ThisFileInfo['mpc']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate']; - $ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major']; + $info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152; + $info['audio']['bitrate'] = $info['mpc']['bitrate']; + $info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major']; return true; } diff --git a/modules/getid3/module.audio.ogg.php b/modules/getid3/module.audio.ogg.php index 968f0b58..98e935bb 100644 --- a/modules/getid3/module.audio.ogg.php +++ b/modules/getid3/module.audio.ogg.php @@ -15,139 +15,220 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true); -class getid3_ogg +class getid3_ogg extends getid3_handler { + var $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 - function getid3_ogg(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'ogg'; + $info['fileformat'] = 'ogg'; // Warn about illegal tags - only vorbiscomments are allowed - if (isset($ThisFileInfo['id3v2'])) { - $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.'; + if (isset($info['id3v2'])) { + $info['warning'][] = 'Illegal ID3v2 tag present.'; } - if (isset($ThisFileInfo['id3v1'])) { - $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.'; + if (isset($info['id3v1'])) { + $info['warning'][] = 'Illegal ID3v1 tag present.'; } - if (isset($ThisFileInfo['ape'])) { - $ThisFileInfo['warning'][] = 'Illegal APE tag present.'; + if (isset($info['ape'])) { + $info['warning'][] = 'Illegal APE tag present.'; } // Page 1 - Stream Header - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) { - $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['ogg']); + if (ftell($this->getid3->fp) >= $this->getid3->fread_buffer_size()) { + $info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)'; + unset($info['fileformat']); + unset($info['ogg']); return false; } - $filedata = fread($fd, $oggpageinfo['page_length']); + $filedata = fread($this->getid3->fp, $oggpageinfo['page_length']); $filedataoffset = 0; if (substr($filedata, 0, 4) == 'fLaC') { - $ThisFileInfo['audio']['dataformat'] = 'flac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; + $info['audio']['dataformat'] = 'flac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; } elseif (substr($filedata, 1, 6) == 'vorbis') { - $this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo); + $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); } elseif (substr($filedata, 0, 8) == 'Speex ') { // http://www.speex.org/manual/node10.html - $ThisFileInfo['audio']['dataformat'] = 'speex'; - $ThisFileInfo['mime_type'] = 'audio/speex'; - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; - $ThisFileInfo['audio']['lossless'] = false; + $info['audio']['dataformat'] = 'speex'; + $info['mime_type'] = 'audio/speex'; + $info['audio']['bitrate_mode'] = 'abr'; + $info['audio']['lossless'] = false; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex ' + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex ' $filedataoffset += 8; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20); $filedataoffset += 20; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']); - $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']; - $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']; - $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']; - $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']); + $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']); + $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']; + $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']; + $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']; + $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']); - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels']; - if ($ThisFileInfo['speex']['vbr']) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['sample_rate'] = $info['speex']['sample_rate']; + $info['audio']['channels'] = $info['speex']['channels']; + if ($info['speex']['vbr']) { + $info['audio']['bitrate_mode'] = 'vbr'; } + + } elseif (substr($filedata, 0, 8) == "fishead\x00") { + + // Ogg Skeleton version 3.0 Format Specification + // http://xiph.org/ogg/doc/skeleton.html + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20)); + $filedataoffset += 20; + + $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor']; + $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator']; + $info['ogg']['skeleton']['fishead']['basetime'] = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator']; + $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc']; + + + $counter = 0; + do { + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo; + $filedata = fread($this->getid3->fp, $oggpageinfo['page_length']); + fseek($this->getid3->fp, $oggpageinfo['page_end_offset'], SEEK_SET); +//echo substr($filedata, 0, 8).'<br>'; + + if (substr($filedata, 0, 8) == "fisbone\x00") { + + $filedataoffset = 8; + $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3); + $filedataoffset += 3; + + } elseif (substr($filedata, 1, 6) == 'theora') { + + $info['video']['dataformat'] = 'theora'; +$info['error'][] = 'Ogg Theora not correctly handled in this version of getID3 ['.$this->getid3->version().']'; +//break; + + } elseif (substr($filedata, 1, 6) == 'vorbis') { + + $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); + + } else { +$info['error'][] = 'unexpected'; +//break; + } + //} while ($oggpageinfo['page_seqno'] == 0); + } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00")); + fseek($this->getid3->fp, $oggpageinfo['page_start_offset'], SEEK_SET); + + +$info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']'; +//return false; + + } else { - $ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither'; - unset($ThisFileInfo['ogg']); - unset($ThisFileInfo['mime_type']); + $info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"'; + unset($info['ogg']); + unset($info['mime_type']); return false; } - // Page 2 - Comment Header + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - - switch ($ThisFileInfo['audio']['dataformat']) { - + switch ($info['audio']['dataformat']) { case 'vorbis': - $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' + $filedata = fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); + $this->ParseVorbisCommentsFilepointer(); break; case 'flac': - if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) { - $ThisFileInfo['error'][] = 'Failed to parse FLAC headers'; + $getid3_flac = new getid3_flac($this->getid3); + if (!$getid3_flac->FLACparseMETAdata()) { + $info['error'][] = 'Failed to parse FLAC headers'; return false; } + unset($getid3_flac); break; case 'speex': - fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); + fseek($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); + $this->ParseVorbisCommentsFilepointer(); break; } @@ -156,66 +237,68 @@ class getid3_ogg // Last Page - Number of Samples - if (!getid3_lib::intValueSupported($ThisFileInfo['avdataend'])) { + if (!getid3_lib::intValueSupported($info['avdataend'])) { - $ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; + $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; } else { - fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET); - $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE)); + fseek($this->getid3->fp, max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0), SEEK_SET); + $LastChunkOfOgg = strrev(fread($this->getid3->fp, $this->getid3->fread_buffer_size())); if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { - fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET); - $ThisFileInfo['avdataend'] = ftell($fd); - $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position']; - if ($ThisFileInfo['ogg']['samples'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero'; + fseek($this->getid3->fp, $info['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET); + $info['avdataend'] = ftell($this->getid3->fp); + $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader(); + $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position']; + if ($info['ogg']['samples'] == 0) { + $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero'; return false; } - $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']); + if (!empty($info['audio']['sample_rate'])) { + $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']); + } } } - if (!empty($ThisFileInfo['ogg']['bitrate_average'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average']; - } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal']; - } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) { - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2; + if (!empty($info['ogg']['bitrate_average'])) { + $info['audio']['bitrate'] = $info['ogg']['bitrate_average']; + } elseif (!empty($info['ogg']['bitrate_nominal'])) { + $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal']; + } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) { + $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2; } - if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) { - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero'; + if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) { + if ($info['audio']['bitrate'] == 0) { + $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero'; return false; } - $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']); + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']); } - if (isset($ThisFileInfo['ogg']['vendor'])) { - $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']); + if (isset($info['ogg']['vendor'])) { + $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']); // Vorbis only - if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') { + if ($info['audio']['dataformat'] == 'vorbis') { // Vorbis 1.0 starts with Xiph.Org - if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) { + if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) { - if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') { + if ($info['audio']['bitrate_mode'] == 'abr') { // Set -b 128 on abr files - $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000); + $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000); - } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) { + } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) { // Set -q N on vbr files - $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']); + $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']); } } - if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) { - $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps'; + if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) { + $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps'; } } } @@ -223,67 +306,70 @@ class getid3_ogg return true; } - static function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) { - $ThisFileInfo['audio']['dataformat'] = 'vorbis'; - $ThisFileInfo['audio']['lossless'] = false; + function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) { + $info = &$this->getid3->info; + $info['audio']['dataformat'] = 'vorbis'; + $info['audio']['lossless'] = false; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); $filedataoffset += 1; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' $filedataoffset += 6; - $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); $filedataoffset += 1; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels']; - $ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['audio']['channels'] = $info['ogg']['numberofchannels']; + $info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - if ($ThisFileInfo['ogg']['samplerate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero'; + if ($info['ogg']['samplerate'] == 0) { + $info['error'][] = 'Corrupt Ogg file: sample rate == zero'; return false; } - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate']; - $ThisFileInfo['ogg']['samples'] = 0; // filled in later - $ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later - $ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['audio']['sample_rate'] = $info['ogg']['samplerate']; + $info['ogg']['samples'] = 0; // filled in later + $info['ogg']['bitrate_average'] = 0; // filled in later + $info['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; - $ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F); - $ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4); - $ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet - - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr - if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_max']); - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; + $info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F); + $info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4); + $info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet + + $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr + if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_max']); + $info['audio']['bitrate_mode'] = 'abr'; } - if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_nominal']); + if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_nominal']); } - if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_min']); - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; + if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_min']); + $info['audio']['bitrate_mode'] = 'abr'; } return true; } - static function ParseOggPageHeader(&$fd) { + function ParseOggPageHeader() { // http://xiph.org/ogg/vorbis/doc/framing.html - $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file + $oggheader['page_start_offset'] = ftell($this->getid3->fp); // where we started from in the file +//echo $oggheader['page_start_offset'].'<br>'; - $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + $filedata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $filedataoffset = 0; while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) { - if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) { + if ((ftell($this->getid3->fp) - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) { // should be found before here return false; } if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { - if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) { + if (feof($this->getid3->fp) || (($filedata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size())) === false)) { // get some more data, unless eof, in which case fail +echo __LINE__.'<br>'; return false; } } @@ -316,39 +402,40 @@ class getid3_ogg } $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset; $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length']; - fseek($fd, $oggheader['header_end_offset'], SEEK_SET); + fseek($this->getid3->fp, $oggheader['header_end_offset'], SEEK_SET); return $oggheader; } - static function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) { + function ParseVorbisCommentsFilepointer() { + $info = &$this->getid3->info; - $OriginalOffset = ftell($fd); - $CommentStartOffset = $OriginalOffset; + $OriginalOffset = ftell($this->getid3->fp); $commentdataoffset = 0; $VorbisCommentPage = 1; - switch ($ThisFileInfo['audio']['dataformat']) { + switch ($info['audio']['dataformat']) { case 'vorbis': - $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block - fseek($fd, $CommentStartOffset, SEEK_SET); - $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; - $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); + $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block + fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET); + $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; + $commentdata = fread($this->getid3->fp, getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); $commentdataoffset += (strlen('vorbis') + 1); break; case 'flac': - fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET); - $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']); + $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4; + fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET); + $commentdata = fread($this->getid3->fp, $info['flac']['VORBIS_COMMENT']['raw']['block_length']); break; case 'speex': - $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block - fseek($fd, $CommentStartOffset, SEEK_SET); - $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; - $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); + $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block + fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET); + $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; + $commentdata = fread($this->getid3->fp, getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); break; default: @@ -359,58 +446,60 @@ class getid3_ogg $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; - $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); + $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); $commentdataoffset += $VendorSize; $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; - $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset; + $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset; $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); - $ThisFileInfo_ogg_comments_raw = &$ThisFileInfo['ogg']['comments_raw']; + $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw']; for ($i = 0; $i < $CommentsCount; $i++) { +//echo $i.' :: '.$CommentStartOffset.' + '.$commentdataoffset.' = '.($CommentStartOffset + $commentdataoffset).'<br>'; $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; - if (ftell($fd) < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) { - $VorbisCommentPage++; + if (ftell($this->getid3->fp) < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) { + if ($oggpageinfo = $this->ParseOggPageHeader()) { + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + $VorbisCommentPage++; - // First, save what we haven't read yet - $AsYetUnusedData = substr($commentdata, $commentdataoffset); + // First, save what we haven't read yet + $AsYetUnusedData = substr($commentdata, $commentdataoffset); - // Then take that data off the end - $commentdata = substr($commentdata, 0, $commentdataoffset); + // Then take that data off the end + $commentdata = substr($commentdata, 0, $commentdataoffset); - // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct - $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct + $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - // Finally, stick the unused data back on the end - $commentdata .= $AsYetUnusedData; + // Finally, stick the unused data back on the end + $commentdata .= $AsYetUnusedData; - //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1)); + //$commentdata .= fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $commentdata .= fread($this->getid3->fp, $this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1)); + } } $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); // replace avdataoffset with position just after the last vorbiscomment - $ThisFileInfo['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4; + $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4; $commentdataoffset += 4; while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) { - if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) { - $ThisFileInfo['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments'; + if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) { + $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments'; break 2; } $VorbisCommentPage++; - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; // First, save what we haven't read yet $AsYetUnusedData = substr($commentdata, $commentdataoffset); @@ -419,40 +508,42 @@ class getid3_ogg $commentdata = substr($commentdata, 0, $commentdataoffset); // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct - $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); // Finally, stick the unused data back on the end $commentdata .= $AsYetUnusedData; - //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - if (!isset($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage])) { - $ThisFileInfo['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd); + //$commentdata .= fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) { + $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($this->getid3->fp); break; } - $readlength = getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1); + $readlength = getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1); if ($readlength <= 0) { - $ThisFileInfo['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd); + $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($this->getid3->fp); break; } - $commentdata .= fread($fd, $readlength); + $commentdata .= fread($this->getid3->fp, $readlength); //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; } + $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset; $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']); $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size']; if (!$commentstring) { // no comment? - $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']'; + $info['warning'][] = 'Blank Ogg comment ['.$i.']'; } elseif (strstr($commentstring, '=')) { $commentexploded = explode('=', $commentstring, 2); - $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); - $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : ''); - $ThisFileInfo_ogg_comments_raw[$i]['data'] = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']); + $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); + $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : ''); + $ThisFileInfo_ogg_comments_raw[$i]['data'] = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']); + $ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($ThisFileInfo_ogg_comments_raw[$i]['data']); if (preg_match('#^(BM|GIF|\xFF\xD8\xFF|\x89\x50\x4E\x47\x0D\x0A\x1A\x0A|II\x2A\x00|MM\x00\x2A)#s', $ThisFileInfo_ogg_comments_raw[$i]['data'])) { $imageinfo = array(); @@ -465,16 +556,56 @@ class getid3_ogg } } } + if (isset($ThisFileInfo_ogg_comments_raw[$i]['value'])) { unset($ThisFileInfo_ogg_comments_raw[$i]['data']); - $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; + $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; } else { - $ThisFileInfo['ogg']['comments']['picture'][] = array('data'=>$ThisFileInfo_ogg_comments_raw[$i]['data'], 'image_mime'=>$ThisFileInfo_ogg_comments_raw[$i]['image_mime']); + do { + if ($this->inline_attachments === false) { + // skip entirely + unset($ThisFileInfo_ogg_comments_raw[$i]['data']); + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $ThisFileInfo_ogg_comments_raw[$i]['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' is too large to process inline ('.number_format($ThisFileInfo_ogg_comments_raw[$i]['data_length']).' bytes)'; + unset($ThisFileInfo_ogg_comments_raw[$i]['data']); + break; + } + } 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)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; + unset($ThisFileInfo_ogg_comments_raw[$i]['data']); + break; + } + } + // if we get this far, must be OK + if (is_string($this->inline_attachments)) { + $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$ThisFileInfo_ogg_comments_raw[$i]['offset']; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $ThisFileInfo_ogg_comments_raw[$i]['data']); + } else { + $info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $ThisFileInfo_ogg_comments_raw[$i]['data_filename'] = $destination_filename; + unset($ThisFileInfo_ogg_comments_raw[$i]['data']); + } else { + $info['ogg']['comments']['picture'][] = array('data'=>$ThisFileInfo_ogg_comments_raw[$i]['data'], 'image_mime'=>$ThisFileInfo_ogg_comments_raw[$i]['image_mime']); + } + } while (false); + } + } else { - $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; + $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; } } @@ -482,32 +613,36 @@ class getid3_ogg // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/ - if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) { - foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) { + if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) { + foreach ($info['ogg']['comments'] as $index => $commentvalue) { switch ($index) { case 'rg_audiophile': case 'replaygain_album_gain': - $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); + $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); break; case 'rg_radio': case 'replaygain_track_gain': - $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); + $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); break; case 'replaygain_album_peak': - $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); + $info['replay_gain']['album']['peak'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); break; case 'rg_peak': case 'replaygain_track_peak': - $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); + $info['replay_gain']['track']['peak'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); break; + case 'replaygain_reference_loudness': + $info['replay_gain']['reference_volume'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; default: // do nothing @@ -516,7 +651,7 @@ class getid3_ogg } } - fseek($fd, $OriginalOffset, SEEK_SET); + fseek($this->getid3->fp, $OriginalOffset, SEEK_SET); return true; } diff --git a/modules/getid3/module.audio.optimfrog.php b/modules/getid3/module.audio.optimfrog.php index 3c2dfb0b..c1c89638 100644 --- a/modules/getid3/module.audio.optimfrog.php +++ b/modules/getid3/module.audio.optimfrog.php @@ -15,39 +15,42 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); -class getid3_optimfrog +class getid3_optimfrog extends getid3_handler { - function getid3_optimfrog(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'ofr'; - $ThisFileInfo['audio']['dataformat'] = 'ofr'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $OFRheader = fread($fd, 8); + $info['fileformat'] = 'ofr'; + $info['audio']['dataformat'] = 'ofr'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $OFRheader = fread($this->getid3->fp, 8); if (substr($OFRheader, 0, 5) == '*RIFF') { - return $this->ParseOptimFROGheader42($fd, $ThisFileInfo); + return $this->ParseOptimFROGheader42(); } elseif (substr($OFRheader, 0, 3) == 'OFR') { - return $this->ParseOptimFROGheader45($fd, $ThisFileInfo); + return $this->ParseOptimFROGheader45(); } - $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"'; - unset($ThisFileInfo['fileformat']); + $info['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($OFRheader).'"'; + unset($info['fileformat']); return false; } - function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) { + function ParseOptimFROGheader42() { // for fileformat of v4.21 and older - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $OptimFROGheaderData = fread($fd, 45); - $ThisFileInfo['avdataoffset'] = 45; + $info = &$this->getid3->info; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $OptimFROGheaderData = fread($this->getid3->fp, 45); + $info['avdataoffset'] = 45; $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1)); $OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10); @@ -57,36 +60,46 @@ class getid3_optimfrog $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { - $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); - fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); - $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); + $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); + $RIFFdata .= fread($this->getid3->fp, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); } // move the data chunk after all other chunks (if any) // so that the RIFF parser doesn't see EOF when trying // to skip over the data chunk $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); - $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; - $ThisFileInfo['playtime_seconds'] = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->ParseRIFFdata($RIFFdata); + $info['riff'] = $getid3_temp->info['riff']; + + $info['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor; + $info['audio']['channels'] = $info['riff']['audio'][0]['channels']; + $info['audio']['sample_rate'] = $info['riff']['audio'][0]['sample_rate']; + $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample']; + $info['playtime_seconds'] = $OrignalRIFFdataSize / ($info['audio']['channels'] * $info['audio']['sample_rate'] * ($info['audio']['bits_per_sample'] / 8)); + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + + unset($getid3_riff, $getid3_temp, $RIFFdata); return true; } - function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) { + function ParseOptimFROGheader45() { // for fileformat of v4.50a and higher + $info = &$this->getid3->info; $RIFFdata = ''; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) { - $BlockOffset = ftell($fd); - $BlockData = fread($fd, 8); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + while (!feof($this->getid3->fp) && (ftell($this->getid3->fp) < $info['avdataend'])) { + $BlockOffset = ftell($this->getid3->fp); + $BlockData = fread($this->getid3->fp, 8); $offset = 8; $BlockName = substr($BlockData, 0, 4); $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); @@ -94,10 +107,10 @@ class getid3_optimfrog if ($BlockName == 'OFRX') { $BlockName = 'OFR '; } - if (!isset($ThisFileInfo['ofr'][$BlockName])) { - $ThisFileInfo['ofr'][$BlockName] = array(); + if (!isset($info['ofr'][$BlockName])) { + $info['ofr'][$BlockName] = array(); } - $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName]; + $thisfile_ofr_thisblock = &$info['ofr'][$BlockName]; switch ($BlockName) { case 'OFR ': @@ -106,7 +119,7 @@ class getid3_optimfrog $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; - $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha'; + $info['audio']['encoder'] = 'OptimFROG 4.50 alpha'; switch ($BlockSize) { case 12: case 15: @@ -114,10 +127,10 @@ class getid3_optimfrog break; default: - $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)'; + $info['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)'; break; } - $BlockData .= fread($fd, $BlockSize); + $BlockData .= fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6)); $offset += 6; @@ -142,23 +155,23 @@ class getid3_optimfrog $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']); $offset += 1; - $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder']; - $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression']; + $info['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder']; + $info['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression']; if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507 - if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') { + if (strtolower(getid3_lib::fileextension($info['filename'])) == 'ofs') { // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference // between lossless and lossy other than the file extension. - $ThisFileInfo['audio']['dataformat'] = 'ofs'; - $ThisFileInfo['audio']['lossless'] = true; + $info['audio']['dataformat'] = 'ofs'; + $info['audio']['lossless'] = true; } } } - $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); + $info['audio']['channels'] = $thisfile_ofr_thisblock['channels']; + $info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; + $info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); break; @@ -168,13 +181,13 @@ class getid3_optimfrog $COMPdata['offset'] = $BlockOffset; $COMPdata['size'] = $BlockSize; - if ($ThisFileInfo['avdataoffset'] == 0) { - $ThisFileInfo['avdataoffset'] = $BlockOffset; + if ($info['avdataoffset'] == 0) { + $info['avdataoffset'] = $BlockOffset; } // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data - $BlockData .= fread($fd, 14); - fseek($fd, $BlockSize - 14, SEEK_CUR); + $BlockData .= fread($this->getid3->fp, 14); + fseek($this->getid3->fp, $BlockSize - 14, SEEK_CUR); $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; @@ -190,7 +203,7 @@ class getid3_optimfrog //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); $offset += 2; - if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) { + if ($info['ofr']['OFR ']['size'] > 12) { // OFR 4.504b or higher $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); @@ -211,7 +224,7 @@ class getid3_optimfrog $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; - $RIFFdata .= fread($fd, $BlockSize); + $RIFFdata .= fread($this->getid3->fp, $BlockSize); break; case 'TAIL': @@ -219,7 +232,7 @@ class getid3_optimfrog $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize > 0) { - $RIFFdata .= fread($fd, $BlockSize); + $RIFFdata .= fread($this->getid3->fp, $BlockSize); } break; @@ -229,7 +242,7 @@ class getid3_optimfrog $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; - fseek($fd, $BlockSize, SEEK_CUR); + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; @@ -238,9 +251,9 @@ class getid3_optimfrog $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; - $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()'; + $info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.$this->getid3->version().') of getID3()'; - fseek($fd, $BlockSize, SEEK_CUR); + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; @@ -252,14 +265,14 @@ class getid3_optimfrog if ($BlockSize == 16) { - $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize); + $thisfile_ofr_thisblock['md5_binary'] = fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); - $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; + $info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; } else { - $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead'; - fseek($fd, $BlockSize, SEEK_CUR); + $info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead'; + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); } break; @@ -269,29 +282,38 @@ class getid3_optimfrog $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; - $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']; - fseek($fd, $BlockSize, SEEK_CUR); + $info['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']; + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; } } - if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) { - $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset']; + if (isset($info['ofr']['TAIL']['offset'])) { + $info['avdataend'] = $info['ofr']['TAIL']['offset']; } - $ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']); - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = (float) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']); + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; // move the data chunk after all other chunks (if any) // so that the RIFF parser doesn't see EOF when trying // to skip over the data chunk $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->ParseRIFFdata($RIFFdata); + $info['riff'] = $getid3_temp->info['riff']; + + unset($getid3_riff, $getid3_temp, $RIFFdata); return true; } - function OptimFROGsampleTypeLookup($SampleType) { + static function OptimFROGsampleTypeLookup($SampleType) { static $OptimFROGsampleTypeLookup = array( 0 => 'unsigned int (8-bit)', 1 => 'signed int (8-bit)', @@ -308,7 +330,7 @@ class getid3_optimfrog return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false); } - function OptimFROGbitsPerSampleTypeLookup($SampleType) { + static function OptimFROGbitsPerSampleTypeLookup($SampleType) { static $OptimFROGbitsPerSampleTypeLookup = array( 0 => 8, 1 => 8, @@ -325,7 +347,7 @@ class getid3_optimfrog return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false); } - function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { + static function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { static $OptimFROGchannelConfigurationLookup = array( 0 => 'mono', 1 => 'stereo' @@ -333,7 +355,7 @@ class getid3_optimfrog return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false); } - function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { + static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { static $OptimFROGchannelConfigNumChannelsLookup = array( 0 => 1, 1 => 2 @@ -343,13 +365,13 @@ class getid3_optimfrog - // function OptimFROGalgorithmNameLookup($AlgorithID) { + // static function OptimFROGalgorithmNameLookup($AlgorithID) { // static $OptimFROGalgorithmNameLookup = array(); // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false); // } - function OptimFROGencoderNameLookup($EncoderID) { + static function OptimFROGencoderNameLookup($EncoderID) { // version = (encoderID >> 4) + 4500 // system = encoderID & 0xF @@ -364,7 +386,7 @@ class getid3_optimfrog return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')'; } - function OptimFROGcompressionLookup($CompressionID) { + static function OptimFROGcompressionLookup($CompressionID) { // mode = compression >> 3 // speedup = compression & 0x07 @@ -386,7 +408,7 @@ class getid3_optimfrog return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')'); } - function OptimFROGspeedupLookup($CompressionID) { + static function OptimFROGspeedupLookup($CompressionID) { // mode = compression >> 3 // speedup = compression & 0x07 @@ -398,7 +420,6 @@ class getid3_optimfrog 0x01 => '2x', 0x02 => '4x' ); - return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID)); } diff --git a/modules/getid3/module.audio.rkau.php b/modules/getid3/module.audio.rkau.php index 0e344d43..c4420762 100644 --- a/modules/getid3/module.audio.rkau.php +++ b/modules/getid3/module.audio.rkau.php @@ -14,60 +14,62 @@ ///////////////////////////////////////////////////////////////// -class getid3_rkau +class getid3_rkau extends getid3_handler { - function getid3_rkau(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $RKAUHeader = fread($fd, 20); - if (substr($RKAUHeader, 0, 3) != 'RKA') { - $ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"'; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $RKAUHeader = fread($this->getid3->fp, 20); + $magic = 'RKA'; + if (substr($RKAUHeader, 0, 3) != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"'; return false; } - $ThisFileInfo['fileformat'] = 'rkau'; - $ThisFileInfo['audio']['dataformat'] = 'rkau'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['fileformat'] = 'rkau'; + $info['audio']['dataformat'] = 'rkau'; + $info['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1)); - $ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); - if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) { - $ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')'; - unset($ThisFileInfo['rkau']); + $info['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1)); + $info['rkau']['version'] = '1.'.str_pad($info['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); + if (($info['rkau']['version'] > 1.07) || ($info['rkau']['version'] < 1.06)) { + $info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] can only parse RKAU files v1.06 and 1.07 (this file is v'.$info['rkau']['version'].')'; + unset($info['rkau']); return false; } - $ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4)); - $ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4)); - $ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1)); - $ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1)); + $info['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4)); + $info['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4)); + $info['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1)); + $info['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1)); - $ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1)); - $this->RKAUqualityLookup($ThisFileInfo['rkau']); + $info['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1)); + $this->RKAUqualityLookup($info['rkau']); - $ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1)); - $ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01)); - $ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02); - $ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04); + $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']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02); + $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04); - if ($ThisFileInfo['rkau']['flags']['streaming']) { - $ThisFileInfo['avdataoffset'] += 20; - $ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4)); + if ($info['rkau']['flags']['streaming']) { + $info['avdataoffset'] += 20; + $info['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4)); } else { - $ThisFileInfo['avdataoffset'] += 16; - $ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1; + $info['avdataoffset'] += 16; + $info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1; } // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes, // sometimes it's more, sometimes less. No idea why(?) - $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate']; + $info['audio']['lossless'] = $info['rkau']['lossless']; + $info['audio']['channels'] = $info['rkau']['channels']; + $info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample']; + $info['audio']['sample_rate'] = $info['rkau']['sample_rate']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8)); + $info['audio']['bitrate'] = ($info['rkau']['compressed_bytes'] * 8) / $info['playtime_seconds']; return true; diff --git a/modules/getid3/module.audio.shorten.php b/modules/getid3/module.audio.shorten.php index bd80c59b..7b3d312e 100644 --- a/modules/getid3/module.audio.shorten.php +++ b/modules/getid3/module.audio.shorten.php @@ -14,36 +14,39 @@ ///////////////////////////////////////////////////////////////// -class getid3_shorten +class getid3_shorten extends getid3_handler { - function getid3_shorten(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); - $ShortenHeader = fread($fd, 8); - if (substr($ShortenHeader, 0, 4) != 'ajkg') { - $ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"'; + $ShortenHeader = fread($this->getid3->fp, 8); + $magic = 'ajkg'; + if (substr($ShortenHeader, 0, 4) != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"'; return false; } - $ThisFileInfo['fileformat'] = 'shn'; - $ThisFileInfo['audio']['dataformat'] = 'shn'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - $ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); - - fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET); - $SeekTableSignatureTest = fread($fd, 12); - $ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); - if ($ThisFileInfo['shn']['seektable']['present']) { - $ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); - $ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length']; - fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET); - $SeekTableMagic = fread($fd, 4); - if ($SeekTableMagic != 'SEEK') { - - $ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"'; + $info['fileformat'] = 'shn'; + $info['audio']['dataformat'] = 'shn'; + $info['audio']['lossless'] = true; + $info['audio']['bitrate_mode'] = 'vbr'; + + $info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); + + fseek($this->getid3->fp, $info['avdataend'] - 12, SEEK_SET); + $SeekTableSignatureTest = fread($this->getid3->fp, 12); + $info['shn']['seektable']['present'] = (bool) (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']; + fseek($this->getid3->fp, $info['shn']['seektable']['offset'], SEEK_SET); + $SeekTableMagic = fread($this->getid3->fp, 4); + $magic = 'SEEK'; + if ($SeekTableMagic != $magic) { + + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['shn']['seektable']['offset'].', found "'.getid3_lib::PrintHexBytes($SeekTableMagic).'"'; return false; } else { @@ -64,11 +67,11 @@ class getid3_shorten // long Offset1[4]; // }TSeekEntry; - $SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16); - $ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); - //$ThisFileInfo['shn']['seektable']['entries'] = array(); + $SeekTableData = fread($this->getid3->fp, $info['shn']['seektable']['length'] - 16); + $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); + //$info['shn']['seektable']['entries'] = array(); //$SeekTableOffset = 0; - //for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) { + //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) { // $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); @@ -102,7 +105,7 @@ class getid3_shorten // $SeekTableOffset += 4; // } // - // $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry; + // $info['shn']['seektable']['entries'][] = $SeekTableEntry; //} } @@ -110,7 +113,7 @@ class getid3_shorten } if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { - $ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'; + $info['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'; return false; } @@ -119,11 +122,11 @@ class getid3_shorten $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); foreach ($RequiredFiles as $required_file) { if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { - $ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist'; + $info['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist'; return false; } } - $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64'; + $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$info['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64'; $commandline = str_replace('/', '\\', $commandline); } else { @@ -133,10 +136,10 @@ class getid3_shorten $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; } if (!$shorten_present) { - $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin'; + $info['error'][] = 'shorten binary was not found in path or /usr/local/bin'; return false; } - $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64'; + $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($info['filenamepath']).' - | head -c 64'; } @@ -148,26 +151,26 @@ class getid3_shorten $fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4)); $DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size)); - $ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; + $info['audio']['channels'] = $DecodedWAVFORMATEX['channels']; + $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; + $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; if (substr($output, 20 + $fmt_size, 4) == 'data') { - $ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; + $info['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; } else { - $ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; + $info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; return false; } - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8; } else { - $ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing'; + $info['error'][] = 'shorten failed to decode file to WAV for parsing'; return false; } diff --git a/modules/getid3/module.audio.tta.php b/modules/getid3/module.audio.tta.php index 903de6bf..1c646ee6 100644 --- a/modules/getid3/module.audio.tta.php +++ b/modules/getid3/module.audio.tta.php @@ -14,25 +14,27 @@ ///////////////////////////////////////////////////////////////// -class getid3_tta +class getid3_tta extends getid3_handler { - function getid3_tta(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'tta'; - $ThisFileInfo['audio']['dataformat'] = 'tta'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['fileformat'] = 'tta'; + $info['audio']['dataformat'] = 'tta'; + $info['audio']['lossless'] = true; + $info['audio']['bitrate_mode'] = 'vbr'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $ttaheader = fread($fd, 26); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $ttaheader = fread($this->getid3->fp, 26); - $ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3); - if ($ThisFileInfo['tta']['magic'] != 'TTA') { - $ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['tta']); + $info['tta']['magic'] = substr($ttaheader, 0, 3); + $magic = 'TTA'; + if ($info['tta']['magic'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['tta']['magic']).'"'; + unset($info['fileformat']); + unset($info['audio']); + unset($info['tta']); return false; } @@ -41,62 +43,62 @@ class getid3_tta case "\x02": // TTA v1.x case "\x03": // TTA v1.x // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year." - $ThisFileInfo['tta']['major_version'] = 1; - $ThisFileInfo['avdataoffset'] += 16; + $info['tta']['major_version'] = 1; + $info['avdataoffset'] += 16; - $ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3}); - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4)); - $ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); + $info['tta']['compression_level'] = ord($ttaheader{3}); + $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); + $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4)); + $info['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); - $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate']; + $info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level']; + $info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate']; break; case '2': // TTA v2.x // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4." - $ThisFileInfo['tta']['major_version'] = 2; - $ThisFileInfo['avdataoffset'] += 20; - - $ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); - $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); - $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4)); - - $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; + $info['tta']['major_version'] = 2; + $info['avdataoffset'] += 20; + + $info['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); + $info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); + $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2)); + $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); + $info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4)); + + $info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level']; + $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; break; case '1': // TTA v3.x // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." - $ThisFileInfo['tta']['major_version'] = 3; - $ThisFileInfo['avdataoffset'] += 26; - - $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup() - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); - $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4)); - $ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4); - $ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4)); - - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; + $info['tta']['major_version'] = 3; + $info['avdataoffset'] += 26; + + $info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup() + $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); + $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); + $info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4)); + $info['tta']['crc32_footer'] = substr($ttaheader, 18, 4); + $info['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4)); + + $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; break; default: - $ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3}; + $info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3}; return false; break; } - $ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['audio']['encoder'] = 'TTA v'.$info['tta']['major_version']; + $info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample']; + $info['audio']['sample_rate'] = $info['tta']['sample_rate']; + $info['audio']['channels'] = $info['tta']['channels']; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; return true; } diff --git a/modules/getid3/module.audio.voc.php b/modules/getid3/module.audio.voc.php index a538a4ae..1186a4cc 100644 --- a/modules/getid3/module.audio.voc.php +++ b/modules/getid3/module.audio.voc.php @@ -14,26 +14,28 @@ ///////////////////////////////////////////////////////////////// -class getid3_voc +class getid3_voc extends getid3_handler { - function getid3_voc(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $OriginalAVdataOffset = $ThisFileInfo['avdataoffset']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $VOCheader = fread($fd, 26); + $OriginalAVdataOffset = $info['avdataoffset']; + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $VOCheader = fread($this->getid3->fp, 26); - if (substr($VOCheader, 0, 19) != 'Creative Voice File') { - $ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"'; + $magic = 'Creative Voice File'; + if (substr($VOCheader, 0, 19) != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($VOCheader, 0, 19)).'"'; return false; } // shortcuts - $thisfile_audio = &$ThisFileInfo['audio']; - $ThisFileInfo['voc'] = array(); - $thisfile_voc = &$ThisFileInfo['voc']; + $thisfile_audio = &$info['audio']; + $info['voc'] = array(); + $thisfile_voc = &$info['voc']; - $ThisFileInfo['fileformat'] = 'voc'; + $info['fileformat'] = 'voc'; $thisfile_audio['dataformat'] = 'voc'; $thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio['lossless'] = true; @@ -54,8 +56,8 @@ class getid3_voc do { - $BlockOffset = ftell($fd); - $BlockData = fread($fd, 4); + $BlockOffset = ftell($this->getid3->fp); + $BlockData = fread($this->getid3->fp, 4); $BlockType = ord($BlockData{0}); $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); $ThisBlock = array(); @@ -67,11 +69,11 @@ class getid3_voc break; case 1: // Sound data - $BlockData .= fread($fd, 2); - if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { - $ThisFileInfo['avdataoffset'] = ftell($fd); + $BlockData .= fread($this->getid3->fp, 2); + if ($info['avdataoffset'] <= $OriginalAVdataOffset) { + $info['avdataoffset'] = ftell($this->getid3->fp); } - fseek($fd, $BlockSize - 2, SEEK_CUR); + fseek($this->getid3->fp, $BlockSize - 2, SEEK_CUR); $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); @@ -94,11 +96,11 @@ class getid3_voc case 6: // Repeat case 7: // End repeat // nothing useful, just skip - fseek($fd, $BlockSize, SEEK_CUR); + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; case 8: // Extended - $BlockData .= fread($fd, 4); + $BlockData .= fread($this->getid3->fp, 4); //00-01 Time Constant: // Mono: 65536 - (256000000 / sample_rate) @@ -112,11 +114,11 @@ class getid3_voc break; case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit - $BlockData .= fread($fd, 12); - if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { - $ThisFileInfo['avdataoffset'] = ftell($fd); + $BlockData .= fread($this->getid3->fp, 12); + if ($info['avdataoffset'] <= $OriginalAVdataOffset) { + $info['avdataoffset'] = ftell($this->getid3->fp); } - fseek($fd, $BlockSize - 12, SEEK_CUR); + fseek($this->getid3->fp, $BlockSize - 12, SEEK_CUR); $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); @@ -134,8 +136,8 @@ class getid3_voc break; default: - $ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; - fseek($fd, $BlockSize, SEEK_CUR); + $info['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; + fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; } @@ -146,16 +148,16 @@ class getid3_voc $thisfile_voc['blocks'][] = $ThisBlock; } - } while (!feof($fd) && ($BlockType != 0)); + } while (!feof($this->getid3->fp) && ($BlockType != 0)); // Terminator block doesn't have size field, so seek back 3 spaces - fseek($fd, -3, SEEK_CUR); + fseek($this->getid3->fp, -3, SEEK_CUR); ksort($thisfile_voc['blocktypes']); if (!empty($thisfile_voc['compressed_bits_per_sample'])) { - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); - $thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); + $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } return true; diff --git a/modules/getid3/module.audio.vqf.php b/modules/getid3/module.audio.vqf.php index 49d4e851..dc6ff5ec 100644 --- a/modules/getid3/module.audio.vqf.php +++ b/modules/getid3/module.audio.vqf.php @@ -14,58 +14,61 @@ ///////////////////////////////////////////////////////////////// -class getid3_vqf +class getid3_vqf extends getid3_handler { - function getid3_vqf(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; + // based loosely on code from TTwinVQ by Jurgen Faul <jfaulŘgmx*de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - $ThisFileInfo['fileformat'] = 'vqf'; - $ThisFileInfo['audio']['dataformat'] = 'vqf'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; + $info['fileformat'] = 'vqf'; + $info['audio']['dataformat'] = 'vqf'; + $info['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['lossless'] = false; // shortcut - $ThisFileInfo['vqf']['raw'] = array(); - $thisfile_vqf = &$ThisFileInfo['vqf']; + $info['vqf']['raw'] = array(); + $thisfile_vqf = &$info['vqf']; $thisfile_vqf_raw = &$thisfile_vqf['raw']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $VQFheaderData = fread($fd, 16); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $VQFheaderData = fread($this->getid3->fp, 16); $offset = 0; - $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); - if ($thisfile_vqf_raw['header_tag'] != 'TWIN') { - $ThisFileInfo['error'][] = 'Expecting "TWIN" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_vqf_raw['header_tag'].'"'; - unset($ThisFileInfo['vqf']); - unset($ThisFileInfo['fileformat']); + $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); + $magic = 'TWIN'; + if ($thisfile_vqf_raw['header_tag'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_vqf_raw['header_tag']).'"'; + unset($info['vqf']); + unset($info['fileformat']); return false; } $offset += 4; - $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); + $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); $offset += 8; - $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); + $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); $offset += 4; - while (ftell($fd) < $ThisFileInfo['avdataend']) { + while (ftell($this->getid3->fp) < $info['avdataend']) { - $ChunkBaseOffset = ftell($fd); + $ChunkBaseOffset = ftell($this->getid3->fp); $chunkoffset = 0; - $ChunkData = fread($fd, 8); + $ChunkData = fread($this->getid3->fp, 8); $ChunkName = substr($ChunkData, $chunkoffset, 4); if ($ChunkName == 'DATA') { - $ThisFileInfo['avdataoffset'] = $ChunkBaseOffset; + $info['avdataoffset'] = $ChunkBaseOffset; break; } $chunkoffset += 4; $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; - if ($ChunkSize > ($ThisFileInfo['avdataend'] - ftell($fd))) { - $ThisFileInfo['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset; + if ($ChunkSize > ($info['avdataend'] - ftell($this->getid3->fp))) { + $info['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset; break; } if ($ChunkSize > 0) { - $ChunkData .= fread($fd, $ChunkSize); + $ChunkData .= fread($this->getid3->fp, $ChunkSize); } switch ($ChunkName) { @@ -83,13 +86,13 @@ class getid3_vqf $thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; - $ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; - $ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; - $ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000); + $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; + $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); + $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; + $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate']/1000); - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; + if ($info['audio']['bitrate'] == 0) { + $info['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; return false; } break; @@ -108,23 +111,23 @@ class getid3_vqf break; default: - $ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset; + $info['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset; break; } } - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; + $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']; - if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) { + if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'))))) { switch ($thisfile_vqf['DSIZ']) { case 0: case 1: - $ThisFileInfo['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0'; - $ThisFileInfo['audio']['encoder'] = 'Ahead Nero'; + $info['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0'; + $info['audio']['encoder'] = 'Ahead Nero'; break; default: - $ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')); + $info['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($info['avdataend'] - $info['avdataoffset'] - strlen('DATA')); break; } } diff --git a/modules/getid3/module.audio.wavpack.php b/modules/getid3/module.audio.wavpack.php index 7cdb861d..6ab5b438 100644 --- a/modules/getid3/module.audio.wavpack.php +++ b/modules/getid3/module.audio.wavpack.php @@ -14,55 +14,56 @@ ///////////////////////////////////////////////////////////////// -class getid3_wavpack +class getid3_wavpack extends getid3_handler { - function getid3_wavpack(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); while (true) { - $wavpackheader = fread($fd, 32); + $wavpackheader = fread($this->getid3->fp, 32); - if (ftell($fd) >= $ThisFileInfo['avdataend']) { + if (ftell($this->getid3->fp) >= $info['avdataend']) { break; - } elseif (feof($fd)) { + } elseif (feof($this->getid3->fp)) { break; } elseif ( - isset($ThisFileInfo['wavpack']['blockheader']['total_samples']) && - isset($ThisFileInfo['wavpack']['blockheader']['block_samples']) && - ($ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) && - ($ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) && - (!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) && - ((isset($ThisFileInfo['wavpack']['config_flags']['md5_checksum']) && ($ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false)) || !empty($ThisFileInfo['md5_data_source']))) { + isset($info['wavpack']['blockheader']['total_samples']) && + isset($info['wavpack']['blockheader']['block_samples']) && + ($info['wavpack']['blockheader']['total_samples'] > 0) && + ($info['wavpack']['blockheader']['block_samples'] > 0) && + (!isset($info['wavpack']['riff_trailer_size']) || ($info['wavpack']['riff_trailer_size'] <= 0)) && + ((isset($info['wavpack']['config_flags']['md5_checksum']) && ($info['wavpack']['config_flags']['md5_checksum'] === false)) || !empty($info['md5_data_source']))) { break; } - $blockheader_offset = ftell($fd) - 32; + $blockheader_offset = ftell($this->getid3->fp) - 32; $blockheader_magic = substr($wavpackheader, 0, 4); $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4)); - if ($blockheader_magic != 'wvpk') { - $ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"'; - switch (isset($ThisFileInfo['audio']['dataformat']) ? $ThisFileInfo['audio']['dataformat'] : '') { + $magic = 'wvpk'; + if ($blockheader_magic != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$blockheader_offset.', found "'.getid3_lib::PrintHexBytes($blockheader_magic).'"'; + switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); + unset($info['fileformat']); + unset($info['audio']); + unset($info['wavpack']); break; } return false; } - - if (empty($ThisFileInfo['wavpack']['blockheader']['block_samples']) || - empty($ThisFileInfo['wavpack']['blockheader']['total_samples']) || - ($ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) || - ($ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) { + if (empty($info['wavpack']['blockheader']['block_samples']) || + empty($info['wavpack']['blockheader']['total_samples']) || + ($info['wavpack']['blockheader']['block_samples'] <= 0) || + ($info['wavpack']['blockheader']['total_samples'] <= 0)) { // Also, it is possible that the first block might not have // any samples (block_samples == 0) and in this case you should skip blocks // until you find one with samples because the other information (like @@ -74,79 +75,79 @@ class getid3_wavpack // the total number of samples. - $ThisFileInfo['audio']['dataformat'] = 'wavpack'; - $ThisFileInfo['fileformat'] = 'wavpack'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['dataformat'] = 'wavpack'; + $info['fileformat'] = 'wavpack'; + $info['audio']['lossless'] = true; + $info['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset; - $ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic; - $ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size; + $info['wavpack']['blockheader']['offset'] = $blockheader_offset; + $info['wavpack']['blockheader']['magic'] = $blockheader_magic; + $info['wavpack']['blockheader']['size'] = $blockheader_size; - if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) { - $ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; - switch (isset($ThisFileInfo['audio']['dataformat']) ? $ThisFileInfo['audio']['dataformat'] : '') { + if ($info['wavpack']['blockheader']['size'] >= 0x100000) { + $info['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$info['wavpack']['blockheader']['size'].'" at offset '.$info['wavpack']['blockheader']['offset']; + switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); + unset($info['fileformat']); + unset($info['audio']); + unset($info['wavpack']); break; } return false; } - $ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8}); - $ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9}); + $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8}); + $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9}); - if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) || - (($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) && - ($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) { - $ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; - switch (isset($ThisFileInfo['audio']['dataformat']) ? $ThisFileInfo['audio']['dataformat'] : '') { + if (($info['wavpack']['blockheader']['major_version'] != 4) || + (($info['wavpack']['blockheader']['minor_version'] < 4) && + ($info['wavpack']['blockheader']['minor_version'] > 16))) { + $info['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$info['wavpack']['blockheader']['major_version'].'.'.$info['wavpack']['blockheader']['minor_version'].'" at offset '.$info['wavpack']['blockheader']['offset']; + switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); + unset($info['fileformat']); + unset($info['audio']); + unset($info['wavpack']); break; } return false; } - $ThisFileInfo['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused - $ThisFileInfo['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused - $ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4)); - $ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4)); - $ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4)); - $ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4)); - $ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4)); - - $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003); - $ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008); - $ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010); - $ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040); - $ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080); - $ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400); - $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800); - $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000); - - $ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']; + $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused + $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused + $info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4)); + $info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4)); + $info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4)); + $info['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4)); + $info['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4)); + + $info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x00000003); + $info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000004); + $info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000008); + $info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000010); + $info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000020); + $info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000040); + $info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000080); + $info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000100); + $info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000200); + $info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000400); + $info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000800); + $info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00001000); + + $info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid']; } - while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) { + while (!feof($this->getid3->fp) && (ftell($this->getid3->fp) < ($blockheader_offset + $blockheader_size + 8))) { - $metablock = array('offset'=>ftell($fd)); - $metablockheader = fread($fd, 2); - if (feof($fd)) { + $metablock = array('offset'=>ftell($this->getid3->fp)); + $metablockheader = fread($this->getid3->fp, 2); + if (feof($this->getid3->fp)) { break; } $metablock['id'] = ord($metablockheader{0}); @@ -165,7 +166,7 @@ class getid3_wavpack $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); $metablock['large_block'] = (bool) ($metablock['id'] & 0x80); if ($metablock['large_block']) { - $metablockheader .= fread($fd, 2); + $metablockheader .= fread($this->getid3->fp, 2); } $metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words $metablock['data'] = null; @@ -179,7 +180,7 @@ class getid3_wavpack case 0x24: // ID_CUESHEET case 0x25: // ID_CONFIG_BLOCK case 0x26: // ID_MD5_CHECKSUM - $metablock['data'] = fread($fd, $metablock['size']); + $metablock['data'] = fread($this->getid3->fp, $metablock['size']); if ($metablock['padded_data']) { // padded to the nearest even byte @@ -202,12 +203,12 @@ class getid3_wavpack case 0x0B: // ID_WVC_BITSTREAM case 0x0C: // ID_WVX_BITSTREAM case 0x0D: // ID_CHANNEL_INFO - fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); + fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; default: - $ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']; - fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); + $info['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']; + fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; } @@ -215,13 +216,18 @@ class getid3_wavpack case 0x21: // ID_RIFF_HEADER getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); $original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4)); - getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader); - $metablock['riff'] = $ParsedRIFFheader['riff']; - $metablock['riff']['original_filesize'] = $original_wav_filesize; - $ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; - $ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->ParseRIFFdata($metablock['data']); + $metablock['riff'] = $getid3_temp->info['riff']; + $info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec']; + unset($getid3_riff, $getid3_temp); + + $metablock['riff']['original_filesize'] = $original_wav_filesize; + $info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; + $info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate']; // Safe RIFF header in case there's a RIFF footer later $metablockRIFFheader = $metablock['data']; @@ -232,26 +238,29 @@ class getid3_wavpack $metablockRIFFfooter = $metablockRIFFheader.$metablock['data']; getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - $ftell_old = ftell($fd); $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); - $ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array()); - $metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter); - fseek($fd, $ftell_old, SEEK_SET); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_temp->info['fileformat'] = 'riff'; + $getid3_riff = new getid3_riff($getid3_temp); + $metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']); if (!empty($metablock['riff']['INFO'])) { - getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); - $ThisFileInfo['tags']['riff'] = $metablock['comments']; + $getid3_riff->RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); + $info['tags']['riff'] = $metablock['comments']; } + unset($getid3_temp, $getid3_riff); break; case 0x23: // ID_REPLAY_GAIN - $ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; + $info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; break; case 0x24: // ID_CUESHEET - $ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; + $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; break; @@ -280,36 +289,36 @@ class getid3_wavpack $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % - $ThisFileInfo['wavpack']['config_flags'] = $metablock['flags']; + $info['wavpack']['config_flags'] = $metablock['flags']; - $ThisFileInfo['audio']['encoder_options'] = ''; - if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) { - $ThisFileInfo['audio']['encoder_options'] .= ' -b???'; + $info['audio']['encoder_options'] = ''; + if ($info['wavpack']['blockheader']['flags']['hybrid']) { + $info['audio']['encoder_options'] .= ' -b???'; } - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : ''); - $ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : ''); - if (!empty($ThisFileInfo['audio']['encoder_options'])) { - $ThisFileInfo['audio']['encoder_options'] = trim($ThisFileInfo['audio']['encoder_options']); - } elseif (isset($ThisFileInfo['audio']['encoder_options'])) { - unset($ThisFileInfo['audio']['encoder_options']); + $info['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : ''); + $info['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : ''); + if (!empty($info['audio']['encoder_options'])) { + $info['audio']['encoder_options'] = trim($info['audio']['encoder_options']); + } elseif (isset($info['audio']['encoder_options'])) { + unset($info['audio']['encoder_options']); } break; case 0x26: // ID_MD5_CHECKSUM if (strlen($metablock['data']) == 16) { - $ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); + $info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); } else { - $ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes'; + $info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes'; } break; @@ -334,24 +343,24 @@ class getid3_wavpack } if (!empty($metablock)) { - $ThisFileInfo['wavpack']['metablocks'][] = $metablock; + $info['wavpack']['metablocks'][] = $metablock; } } } - $ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2); + $info['audio']['encoder'] = 'WavPack v'.$info['wavpack']['blockheader']['major_version'].'.'.str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); + $info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; + $info['audio']['channels'] = ($info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2); - if (!empty($ThisFileInfo['playtime_seconds'])) { + if (!empty($info['playtime_seconds'])) { - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } else { - $ThisFileInfo['audio']['dataformat'] = 'wvc'; + $info['audio']['dataformat'] = 'wvc'; } diff --git a/modules/getid3/module.graphic.bmp.php b/modules/getid3/module.graphic.bmp.php index d3f17da3..5ac671fb 100644 --- a/modules/getid3/module.graphic.bmp.php +++ b/modules/getid3/module.graphic.bmp.php @@ -14,14 +14,17 @@ ///////////////////////////////////////////////////////////////// -class getid3_bmp +class getid3_bmp extends getid3_handler { + var $ExtractPalette = false; + var $ExtractData = false; - function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) { + function Analyze() { + $info = &$this->getid3->info; // shortcuts - $ThisFileInfo['bmp']['header']['raw'] = array(); - $thisfile_bmp = &$ThisFileInfo['bmp']; + $info['bmp']['header']['raw'] = array(); + $thisfile_bmp = &$info['bmp']; $thisfile_bmp_header = &$thisfile_bmp['header']; $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw']; @@ -33,17 +36,18 @@ class getid3_bmp // WORD bfReserved2; // DWORD bfOffBits; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $offset = 0; - $BMPheader = fread($fd, 14 + 40); + $BMPheader = fread($this->getid3->fp, 14 + 40); $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); $offset += 2; - if ($thisfile_bmp_header_raw['identifier'] != 'BM') { - $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['bmp']); + $magic = 'BM'; + if ($thisfile_bmp_header_raw['identifier'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_bmp_header_raw['identifier']).'"'; + unset($info['fileformat']); + unset($info['bmp']); return false; } @@ -81,16 +85,16 @@ class getid3_bmp $thisfile_bmp['type_os'] = 'Windows'; $thisfile_bmp['type_version'] = 5; } else { - $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['bmp']); + $info['error'][] = 'Unknown BMP subtype (or not a BMP file)'; + unset($info['fileformat']); + unset($info['bmp']); return false; } - $ThisFileInfo['fileformat'] = 'bmp'; - $ThisFileInfo['video']['dataformat'] = 'bmp'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $info['fileformat'] = 'bmp'; + $info['video']['dataformat'] = 'bmp'; + $info['video']['lossless'] = true; + $info['video']['pixel_aspect_ratio'] = (float) 1; if ($thisfile_bmp['type_os'] == 'OS/2') { @@ -112,10 +116,10 @@ class getid3_bmp $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; - $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; - $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $info['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; if ($thisfile_bmp['type_version'] >= 2) { // DWORD Compression; /* Bitmap compression scheme */ @@ -164,7 +168,7 @@ class getid3_bmp $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']); - $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $info['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; } } elseif ($thisfile_bmp['type_os'] == 'Windows') { @@ -209,14 +213,14 @@ class getid3_bmp $offset += 4; $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']); - $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; - $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $info['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) { // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen - $BMPheader .= fread($fd, 44); + $BMPheader .= fread($this->getid3->fp, 44); // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp // Win95+, WinNT4.0+ @@ -258,7 +262,7 @@ class getid3_bmp } if ($thisfile_bmp['type_version'] >= 5) { - $BMPheader .= fread($fd, 16); + $BMPheader .= fread($this->getid3->fp, 16); // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp // Win98+, Win2000+ @@ -278,13 +282,13 @@ class getid3_bmp } else { - $ThisFileInfo['error'][] = 'Unknown BMP format in header.'; + $info['error'][] = 'Unknown BMP format in header.'; return false; } - if ($ExtractPalette || $ExtractData) { + if ($this->ExtractPalette || $this->ExtractData) { $PaletteEntries = 0; if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) { $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']); @@ -292,7 +296,7 @@ class getid3_bmp $PaletteEntries = $thisfile_bmp_header_raw['colors_used']; } if ($PaletteEntries > 0) { - $BMPpalette = fread($fd, 4 * $PaletteEntries); + $BMPpalette = fread($this->getid3->fp, 4 * $PaletteEntries); $paletteoffset = 0; for ($i = 0; $i < $PaletteEntries; $i++) { // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp @@ -313,10 +317,10 @@ class getid3_bmp } } - if ($ExtractData) { - fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); + if ($this->ExtractData) { + fseek($this->getid3->fp, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry - $BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength); + $BMPpixelData = fread($this->getid3->fp, $thisfile_bmp_header_raw['height'] * $RowByteLength); $pixeldataoffset = 0; $thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : ''); switch ($thisfile_bmp_header_raw['compression']) { @@ -401,7 +405,7 @@ class getid3_bmp break; default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + $info['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; break; } break; @@ -476,7 +480,7 @@ class getid3_bmp break; default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + $info['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; break; } break; @@ -565,7 +569,7 @@ class getid3_bmp break; default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + $info['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; break; } break; @@ -605,14 +609,14 @@ class getid3_bmp break; default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + $info['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; break; } break; default: // unhandled compression type - $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data'; + $info['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data'; break; } } diff --git a/modules/getid3/module.graphic.efax.php b/modules/getid3/module.graphic.efax.php new file mode 100644 index 00000000..2a57302e --- /dev/null +++ b/modules/getid3/module.graphic.efax.php @@ -0,0 +1,53 @@ +<?php +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich <info@getid3.org> // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.efax.php // +// module for analyzing eFax files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_efax extends getid3_handler +{ + + function Analyze() { + $info = &$this->getid3->info; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $efaxheader = fread($this->getid3->fp, 1024); + + $info['efax']['header']['magic'] = substr($efaxheader, 0, 2); + if ($info['efax']['header']['magic'] != "\xDC\xFE") { + $info['error'][] = 'Invalid eFax byte order identifier (expecting DC FE, found '.getid3_lib::PrintHexBytes($info['efax']['header']['magic']).') at offset '.$info['avdataoffset']; + return false; + } + $info['fileformat'] = 'efax'; + + $info['efax']['header']['filesize'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 2, 4)); + if ($info['efax']['header']['filesize'] != $info['filesize']) { + $info['error'][] = 'Probable '.(($info['efax']['header']['filesize'] > $info['filesize']) ? 'truncated' : 'corrupt').' file, expecting '.$info['efax']['header']['filesize'].' bytes, found '.$info['filesize'].' bytes'; + } + $info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), "\x00"); + $info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), "\x00"); + $info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), "\x00"); + + $info['efax']['header']['pages'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 198, 2)); + $info['efax']['header']['data_bytes'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 202, 4)); + +$info['error'][] = 'eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; +return false; + + return true; + } + +} + + +?>
\ No newline at end of file diff --git a/modules/getid3/module.graphic.gif.php b/modules/getid3/module.graphic.gif.php index 986ada30..0b3e1379 100644 --- a/modules/getid3/module.graphic.gif.php +++ b/modules/getid3/module.graphic.gif.php @@ -14,85 +14,88 @@ ///////////////////////////////////////////////////////////////// -class getid3_gif +class getid3_gif extends getid3_handler { - function getid3_gif(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'gif'; - $ThisFileInfo['video']['dataformat'] = 'gif'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $GIFheader = fread($fd, 13); + $info['fileformat'] = 'gif'; + $info['video']['dataformat'] = 'gif'; + $info['video']['lossless'] = true; + $info['video']['pixel_aspect_ratio'] = (float) 1; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $GIFheader = fread($this->getid3->fp, 13); $offset = 0; - $ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); + $info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); $offset += 3; - if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') { - $ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['gif']); + $magic = 'GIF'; + if ($info['gif']['header']['raw']['identifier'] != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['gif']['header']['raw']['identifier']).'"'; + unset($info['fileformat']); + unset($info['gif']); return false; } - $ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3); + $info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3); $offset += 3; - $ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); + $info['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; - $ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); + $info['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; - $ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $info['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; - $ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $info['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; - $ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $info['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height']; - $ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version']; - $ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80); - if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) { + $info['video']['resolution_x'] = $info['gif']['header']['raw']['width']; + $info['video']['resolution_y'] = $info['gif']['header']['raw']['height']; + $info['gif']['version'] = $info['gif']['header']['raw']['version']; + $info['gif']['header']['flags']['global_color_table'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x80); + if ($info['gif']['header']['raw']['flags'] & 0x80) { // Number of bits per primary color available to the original image, minus 1 - $ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1); + $info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1); } else { - $ThisFileInfo['gif']['header']['bits_per_pixel'] = 0; + $info['gif']['header']['bits_per_pixel'] = 0; } - $ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40); - if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { + $info['gif']['header']['flags']['global_color_sorted'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x40); + if ($info['gif']['header']['flags']['global_color_table']) { // the number of bytes contained in the Global Color Table. To determine that // actual size of the color table, raise 2 to [the value of the field + 1] - $ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1); - $ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1; + $info['gif']['header']['global_color_size'] = pow(2, ($info['gif']['header']['raw']['flags'] & 0x07) + 1); + $info['video']['bits_per_sample'] = ($info['gif']['header']['raw']['flags'] & 0x07) + 1; } else { - $ThisFileInfo['gif']['header']['global_color_size'] = 0; + $info['gif']['header']['global_color_size'] = 0; } - if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) { + if ($info['gif']['header']['raw']['aspect_ratio'] != 0) { // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 - $ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64; + $info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64; } -// if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { -// $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']); +// if ($info['gif']['header']['flags']['global_color_table']) { +// $GIFcolorTable = fread($this->getid3->fp, 3 * $info['gif']['header']['global_color_size']); // $offset = 0; -// for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) { +// 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)); -// $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); +// $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); // } // } // // // Image Descriptor -// while (!feof($fd)) { -// $NextBlockTest = fread($fd, 1); +// while (!feof($this->getid3->fp)) { +// $NextBlockTest = fread($this->getid3->fp, 1); // switch ($NextBlockTest) { // // case ',': // ',' - Image separator character // -// $ImageDescriptorData = $NextBlockTest.fread($fd, 9); +// $ImageDescriptorData = $NextBlockTest.fread($this->getid3->fp, 9); // $ImageDescriptor = array(); // $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); // $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); @@ -101,19 +104,19 @@ class getid3_gif // $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); -// $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor; +// $info['gif']['image_descriptor'][] = $ImageDescriptor; // // if ($ImageDescriptor['flags']['use_local_color_map']) { // -// $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs'; +// $info['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs'; // return true; // // } -//echo 'Start of raster data: '.ftell($fd).'<BR>'; +//echo 'Start of raster data: '.ftell($this->getid3->fp).'<BR>'; // $RasterData = array(); -// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); -// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); -// $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData; +// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); +// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 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++) { @@ -123,16 +126,16 @@ class getid3_gif // $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code // // -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// $NextValue = $this->GetLSBits($CurrentCodeSize); // echo 'Clear Code: '.$NextValue.'<BR>'; // -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// $NextValue = $this->GetLSBits($CurrentCodeSize); // echo 'First Color: '.$NextValue.'<BR>'; // // $Prefix = $NextValue; //$i = 0; // while ($i++ < 20) { -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// $NextValue = $this->GetLSBits($CurrentCodeSize); // echo $NextValue.'<BR>'; // } //return true; @@ -140,16 +143,16 @@ class getid3_gif // // case '!': // // GIF Extension Block -// $ExtensionBlockData = $NextBlockTest.fread($fd, 2); +// $ExtensionBlockData = $NextBlockTest.fread($this->getid3->fp, 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'] = fread($fd, $ExtensionBlock['byte_length']); -// $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock; +// $ExtensionBlock['data'] = fread($this->getid3->fp, $ExtensionBlock['byte_length']); +// $info['gif']['extension_blocks'][] = $ExtensionBlock; // break; // // case ';': -// $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1; +// $info['gif']['terminator_offset'] = ftell($this->getid3->fp) - 1; // // GIF Terminator // break; // @@ -164,13 +167,11 @@ class getid3_gif } - function GetLSBits($fd, $bits) { + function GetLSBits($bits) { static $bitbuffer = ''; while (strlen($bitbuffer) < $bits) { -//echo 'Read another byte: '.ftell($fd).'<BR>'; - $bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer; + $bitbuffer = str_pad(decbin(ord(fread($this->getid3->fp, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer; } - $value = bindec(substr($bitbuffer, 0 - $bits)); $bitbuffer = substr($bitbuffer, 0, 0 - $bits); diff --git a/modules/getid3/module.graphic.jpg.php b/modules/getid3/module.graphic.jpg.php index d74507f7..153ebcd0 100644 --- a/modules/getid3/module.graphic.jpg.php +++ b/modules/getid3/module.graphic.jpg.php @@ -15,21 +15,23 @@ ///////////////////////////////////////////////////////////////// -class getid3_jpg +class getid3_jpg extends getid3_handler { - function getid3_jpg(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'jpg'; - $ThisFileInfo['video']['dataformat'] = 'jpg'; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $info['fileformat'] = 'jpg'; + $info['video']['dataformat'] = 'jpg'; + $info['video']['lossless'] = false; + $info['video']['bits_per_sample'] = 24; + $info['video']['pixel_aspect_ratio'] = (float) 1; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $imageinfo = array(); - list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize']), $imageinfo); + list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($this->getid3->fp, $info['filesize']), $imageinfo); if (isset($imageinfo['APP13'])) { @@ -43,10 +45,10 @@ class getid3_jpg foreach ($iptc_values as $key => $value) { $IPTCrecordName = $this->IPTCrecordName($iptc_record); $IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey); - if (isset($ThisFileInfo['iptc'][$IPTCrecordName][$IPTCrecordTagName])) { - $ThisFileInfo['iptc'][$IPTCrecordName][$IPTCrecordTagName][] = $value; + if (isset($info['iptc'][$IPTCrecordName][$IPTCrecordTagName])) { + $info['iptc'][$IPTCrecordName][$IPTCrecordTagName][] = $value; } else { - $ThisFileInfo['iptc'][$IPTCrecordName][$IPTCrecordTagName] = array($value); + $info['iptc'][$IPTCrecordName][$IPTCrecordTagName] = array($value); } } } @@ -56,25 +58,18 @@ class getid3_jpg $returnOK = false; switch ($type) { case IMG_JPG: - $ThisFileInfo['video']['resolution_x'] = $width; - $ThisFileInfo['video']['resolution_y'] = $height; + $info['video']['resolution_x'] = $width; + $info['video']['resolution_y'] = $height; if (isset($imageinfo['APP1'])) { if (function_exists('exif_read_data')) { if (substr($imageinfo['APP1'], 0, 4) == 'Exif') { - ob_start(); - $ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false); - $errors = ob_get_contents(); - if ($errors) { - $ThisFileInfo['warning'][] = strip_tags($errors); - //unset($ThisFileInfo['jpg']['exif']); - } - ob_end_clean(); + $info['jpg']['exif'] = @exif_read_data($info['filenamepath'], '', true, false); } else { - $ThisFileInfo['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")'; + $info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")'; } } else { - $ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); + $info['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); } } $returnOK = true; @@ -87,25 +82,25 @@ class getid3_jpg $cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL'); foreach ($cast_as_appropriate_keys as $exif_key) { - if (isset($ThisFileInfo['jpg']['exif'][$exif_key])) { - foreach ($ThisFileInfo['jpg']['exif'][$exif_key] as $key => $value) { - $ThisFileInfo['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value); + if (isset($info['jpg']['exif'][$exif_key])) { + foreach ($info['jpg']['exif'][$exif_key] as $key => $value) { + $info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value); } } } - if (isset($ThisFileInfo['jpg']['exif']['GPS'])) { + if (isset($info['jpg']['exif']['GPS'])) { - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSVersion'])) { + if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) { for ($i = 0; $i < 4; $i++) { - $version_subparts[$i] = ord(substr($ThisFileInfo['jpg']['exif']['GPS']['GPSVersion'], $i, 1)); + $version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1)); } - $ThisFileInfo['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts); + $info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts); } - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSDateStamp'])) { - $explodedGPSDateStamp = explode(':', $ThisFileInfo['jpg']['exif']['GPS']['GPSDateStamp']); + if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) { + $explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']); $computed_time[5] = (isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : ''); $computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : ''); $computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : ''); @@ -117,51 +112,51 @@ class getid3_jpg } $computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0); - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp'])) { - foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) { + if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) { + foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) { $computed_time[$key] = getid3_lib::DecimalizeFraction($value); } } - $ThisFileInfo['jpg']['exif']['GPS']['computed']['timestamp'] = mktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]); + $info['jpg']['exif']['GPS']['computed']['timestamp'] = mktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]); } - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude'])) { - $direction_multiplier = ((isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1); - foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) { + 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); + foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) { $computed_latitude[$key] = getid3_lib::DecimalizeFraction($value); } - $ThisFileInfo['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600)); + $info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600)); } - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude'])) { - $direction_multiplier = ((isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1); - foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) { + 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); + foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) { $computed_longitude[$key] = getid3_lib::DecimalizeFraction($value); } - $ThisFileInfo['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600)); + $info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600)); } - if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitude'])) { - $direction_multiplier = ((isset($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitudeRef']) && ($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitudeRef'] === chr(1))) ? -1 : 1); - $ThisFileInfo['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitude']); + if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) { + $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSAltitudeRef']) && ($info['jpg']['exif']['GPS']['GPSAltitudeRef'] === chr(1))) ? -1 : 1); + $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']); } } if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, false)) { - if (isset($ThisFileInfo['filenamepath'])) { - $image_xmp = new Image_XMP($ThisFileInfo['filenamepath']); + if (isset($info['filenamepath'])) { + $image_xmp = new Image_XMP($info['filenamepath']); $xmp_raw = $image_xmp->getAllTags(); foreach ($xmp_raw as $key => $value) { list($subsection, $tagname) = explode(':', $key); - $ThisFileInfo['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); + $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); } } } if (!$returnOK) { - unset($ThisFileInfo['fileformat']); + unset($info['fileformat']); return false; } return true; diff --git a/modules/getid3/module.graphic.pcd.php b/modules/getid3/module.graphic.pcd.php index 60efabdf..dcbc6763 100644 --- a/modules/getid3/module.graphic.pcd.php +++ b/modules/getid3/module.graphic.pcd.php @@ -14,34 +14,38 @@ ///////////////////////////////////////////////////////////////// -class getid3_pcd +class getid3_pcd extends getid3_handler { - function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) { - $ThisFileInfo['fileformat'] = 'pcd'; - $ThisFileInfo['video']['dataformat'] = 'pcd'; - $ThisFileInfo['video']['lossless'] = false; + var $ExtractData = 0; + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET); + $info['fileformat'] = 'pcd'; + $info['video']['dataformat'] = 'pcd'; + $info['video']['lossless'] = false; - $PCDflags = fread($fd, 1); + + fseek($this->getid3->fp, $info['avdataoffset'] + 72, SEEK_SET); + + $PCDflags = fread($this->getid3->fp, 1); $PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false); if ($PCDisVertical) { - $ThisFileInfo['video']['resolution_x'] = 3072; - $ThisFileInfo['video']['resolution_y'] = 2048; + $info['video']['resolution_x'] = 3072; + $info['video']['resolution_y'] = 2048; } else { - $ThisFileInfo['video']['resolution_x'] = 2048; - $ThisFileInfo['video']['resolution_y'] = 3072; + $info['video']['resolution_x'] = 2048; + $info['video']['resolution_y'] = 3072; } - if ($ExtractData > 3) { + if ($this->ExtractData > 3) { - $ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)'; + $info['error'][] = 'Cannot extract PSD image data for detail levels above BASE (level-3) because encrypted with Kodak-proprietary compression/encryption.'; - } elseif ($ExtractData > 0) { + } elseif ($this->ExtractData > 0) { $PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16 $PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4 @@ -52,7 +56,7 @@ class getid3_pcd list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3]; - fseek($fd, $ThisFileInfo['avdataoffset'] + $PCD_dataOffset, SEEK_SET); + fseek($this->getid3->fp, $info['avdataoffset'] + $PCD_dataOffset, SEEK_SET); for ($y = 0; $y < $PCD_height; $y += 2) { // The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h. @@ -61,18 +65,18 @@ class getid3_pcd // the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes // and the second half of the third ‘w’ bytes contain data for a second RGB-line. - $PCD_data_Y1 = fread($fd, $PCD_width); - $PCD_data_Y2 = fread($fd, $PCD_width); - $PCD_data_Cb = fread($fd, intval(round($PCD_width / 2))); - $PCD_data_Cr = fread($fd, intval(round($PCD_width / 2))); + $PCD_data_Y1 = fread($this->getid3->fp, $PCD_width); + $PCD_data_Y2 = fread($this->getid3->fp, $PCD_width); + $PCD_data_Cb = fread($this->getid3->fp, intval(round($PCD_width / 2))); + $PCD_data_Cr = fread($this->getid3->fp, intval(round($PCD_width / 2))); for ($x = 0; $x < $PCD_width; $x++) { if ($PCDisVertical) { - $ThisFileInfo['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)})); - $ThisFileInfo['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{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)})); } else { - $ThisFileInfo['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)})); - $ThisFileInfo['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{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)})); } } } @@ -86,7 +90,7 @@ class getid3_pcd // $BMPinfo['resolution_x'] = $PCD_width; // $BMPinfo['resolution_y'] = $PCD_height; //} - //$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data']; + //$BMPinfo['bmp']['data'] = $info['pcd']['data']; //getid3_bmp::PlotBMP($BMPinfo); //exit; diff --git a/modules/getid3/module.graphic.png.php b/modules/getid3/module.graphic.png.php index c9873cc4..9109c436 100644 --- a/modules/getid3/module.graphic.png.php +++ b/modules/getid3/module.graphic.png.php @@ -14,37 +14,38 @@ ///////////////////////////////////////////////////////////////// -class getid3_png +class getid3_png extends getid3_handler { - function getid3_png(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; // shortcut - $ThisFileInfo['png'] = array(); - $thisfile_png = &$ThisFileInfo['png']; + $info['png'] = array(); + $thisfile_png = &$info['png']; - $ThisFileInfo['fileformat'] = 'png'; - $ThisFileInfo['video']['dataformat'] = 'png'; - $ThisFileInfo['video']['lossless'] = false; + $info['fileformat'] = 'png'; + $info['video']['dataformat'] = 'png'; + $info['video']['lossless'] = false; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $PNGfiledata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $PNGfiledata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $offset = 0; $PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A $offset += 8; if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { - $ThisFileInfo['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier'; - unset($ThisFileInfo['fileformat']); + $info['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier'; + unset($info['fileformat']); return false; } - while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) { + while (((ftell($this->getid3->fp) - (strlen($PNGfiledata) - $offset)) < $info['filesize'])) { $chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); $offset += 4; - while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) { - $PNGfiledata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); + while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($this->getid3->fp) < $info['filesize'])) { + $PNGfiledata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size()); } $chunk['type_text'] = substr($PNGfiledata, $offset, 4); $offset += 4; @@ -80,10 +81,10 @@ class getid3_png $thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02); $thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04); - $ThisFileInfo['video']['resolution_x'] = $thisfile_png_chunk_type_text['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_png_chunk_type_text['height']; + $info['video']['resolution_x'] = $thisfile_png_chunk_type_text['width']; + $info['video']['resolution_y'] = $thisfile_png_chunk_type_text['height']; - $ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']); + $info['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']); break; @@ -123,10 +124,10 @@ class getid3_png case 4: case 6: - $ThisFileInfo['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; + $info['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; default: - $ThisFileInfo['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; + $info['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; break; } break; @@ -429,7 +430,7 @@ class getid3_png default: //unset($chunk['data']); $thisfile_png_chunk_type_text['header'] = $chunk; - $ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text']; + $info['warning'][] = 'Unhandled chunk type: '.$chunk['type_text']; break; } } diff --git a/modules/getid3/module.graphic.svg.php b/modules/getid3/module.graphic.svg.php index 65bdbacb..d9d52d74 100644 --- a/modules/getid3/module.graphic.svg.php +++ b/modules/getid3/module.graphic.svg.php @@ -14,85 +14,87 @@ ///////////////////////////////////////////////////////////////// -class getid3_svg +class getid3_svg extends getid3_handler { - function getid3_svg(&$fd, &$ThisFileInfo) { - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + function Analyze() { + $info = &$this->getid3->info; - $SVGheader = fread($fd, 4096); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + + $SVGheader = fread($this->getid3->fp, 4096); if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) { - $ThisFileInfo['svg']['xml']['raw'] = $matches; + $info['svg']['xml']['raw'] = $matches; } if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) { - $ThisFileInfo['svg']['doctype']['raw'] = $matches; + $info['svg']['doctype']['raw'] = $matches; } if (preg_match('#\<svg([^\>]+)\>#i', $SVGheader, $matches)) { - $ThisFileInfo['svg']['svg']['raw'] = $matches; + $info['svg']['svg']['raw'] = $matches; } - if (isset($ThisFileInfo['svg']['svg']['raw'])) { + if (isset($info['svg']['svg']['raw'])) { $sections_to_fix = array('xml', 'doctype', 'svg'); foreach ($sections_to_fix as $section_to_fix) { - if (!isset($ThisFileInfo['svg'][$section_to_fix])) { + if (!isset($info['svg'][$section_to_fix])) { continue; } $section_data = array(); - while (preg_match('/ "([^"]+)"/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1], $matches)) { + while (preg_match('/ "([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) { $section_data[] = $matches[1]; - $ThisFileInfo['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $ThisFileInfo['svg'][$section_to_fix]['raw'][1]); + $info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]); } - while (preg_match('/([^\s]+)="([^"]+)"/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1], $matches)) { + while (preg_match('/([^\s]+)="([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) { $section_data[] = $matches[0]; - $ThisFileInfo['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $ThisFileInfo['svg'][$section_to_fix]['raw'][1]); + $info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]); } - $section_data = array_merge($section_data, preg_split('/[\s,]+/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1])); + $section_data = array_merge($section_data, preg_split('/[\s,]+/', $info['svg'][$section_to_fix]['raw'][1])); foreach ($section_data as $keyvaluepair) { $keyvaluepair = trim($keyvaluepair); if ($keyvaluepair) { $keyvalueexploded = explode('=', $keyvaluepair); $key = (isset($keyvalueexploded[0]) ? $keyvalueexploded[0] : ''); $value = (isset($keyvalueexploded[1]) ? $keyvalueexploded[1] : ''); - $ThisFileInfo['svg'][$section_to_fix]['sections'][$key] = trim($value, '"'); + $info['svg'][$section_to_fix]['sections'][$key] = trim($value, '"'); } } } - $ThisFileInfo['fileformat'] = 'svg'; - $ThisFileInfo['video']['dataformat'] = 'svg'; - $ThisFileInfo['video']['lossless'] = true; - //$ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + $info['fileformat'] = 'svg'; + $info['video']['dataformat'] = 'svg'; + $info['video']['lossless'] = true; + //$info['video']['bits_per_sample'] = 24; + $info['video']['pixel_aspect_ratio'] = (float) 1; - if (!empty($ThisFileInfo['svg']['svg']['sections']['width'])) { - $ThisFileInfo['svg']['width'] = intval($ThisFileInfo['svg']['svg']['sections']['width']); + if (!empty($info['svg']['svg']['sections']['width'])) { + $info['svg']['width'] = intval($info['svg']['svg']['sections']['width']); } - if (!empty($ThisFileInfo['svg']['svg']['sections']['height'])) { - $ThisFileInfo['svg']['height'] = intval($ThisFileInfo['svg']['svg']['sections']['height']); + if (!empty($info['svg']['svg']['sections']['height'])) { + $info['svg']['height'] = intval($info['svg']['svg']['sections']['height']); } - if (!empty($ThisFileInfo['svg']['svg']['sections']['version'])) { - $ThisFileInfo['svg']['version'] = $ThisFileInfo['svg']['svg']['sections']['version']; + if (!empty($info['svg']['svg']['sections']['version'])) { + $info['svg']['version'] = $info['svg']['svg']['sections']['version']; } - if (!isset($ThisFileInfo['svg']['version']) && isset($ThisFileInfo['svg']['doctype']['sections'])) { - foreach ($ThisFileInfo['svg']['doctype']['sections'] as $key => $value) { + if (!isset($info['svg']['version']) && isset($info['svg']['doctype']['sections'])) { + foreach ($info['svg']['doctype']['sections'] as $key => $value) { if (preg_match('#//W3C//DTD SVG ([0-9\.]+)//#i', $key, $matches)) { - $ThisFileInfo['svg']['version'] = $matches[1]; + $info['svg']['version'] = $matches[1]; break; } } } - if (!empty($ThisFileInfo['svg']['width'])) { - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width']; + if (!empty($info['svg']['width'])) { + $info['video']['resolution_x'] = $info['svg']['width']; } - if (!empty($ThisFileInfo['svg']['height'])) { - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height']; + if (!empty($info['svg']['height'])) { + $info['video']['resolution_y'] = $info['svg']['height']; } return true; } - $ThisFileInfo['error'][] = 'Did not find expected <svg> tag'; + $info['error'][] = 'Did not find expected <svg> tag'; return false; } diff --git a/modules/getid3/module.graphic.tiff.php b/modules/getid3/module.graphic.tiff.php index 971dc552..e9771242 100644 --- a/modules/getid3/module.graphic.tiff.php +++ b/modules/getid3/module.graphic.tiff.php @@ -14,56 +14,57 @@ ///////////////////////////////////////////////////////////////// -class getid3_tiff +class getid3_tiff extends getid3_handler { - function getid3_tiff(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $TIFFheader = fread($fd, 4); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $TIFFheader = fread($this->getid3->fp, 4); switch (substr($TIFFheader, 0, 2)) { case 'II': - $ThisFileInfo['tiff']['byte_order'] = 'Intel'; + $info['tiff']['byte_order'] = 'Intel'; break; case 'MM': - $ThisFileInfo['tiff']['byte_order'] = 'Motorola'; + $info['tiff']['byte_order'] = 'Motorola'; break; default: - $ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset']; + $info['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset']; return false; break; } - $ThisFileInfo['fileformat'] = 'tiff'; - $ThisFileInfo['video']['dataformat'] = 'tiff'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['tiff']['ifd'] = array(); + $info['fileformat'] = 'tiff'; + $info['video']['dataformat'] = 'tiff'; + $info['video']['lossless'] = true; + $info['tiff']['ifd'] = array(); $CurrentIFD = array(); $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); - $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); + $nextIFDoffset = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); while ($nextIFDoffset > 0) { $CurrentIFD['offset'] = $nextIFDoffset; - fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET); - $CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); + fseek($this->getid3->fp, $info['avdataoffset'] + $nextIFDoffset, SEEK_SET); + $CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { - $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['offset'] = fread($fd, 4); + $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['offset'] = fread($this->getid3->fp, 4); switch ($CurrentIFD['fields'][$i]['raw']['type']) { case 1: // BYTE An 8-bit unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']); } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; @@ -71,23 +72,23 @@ class getid3_tiff if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3); } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 3: // SHORT A 16-bit (2-byte) unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']); } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 4: // LONG A 32-bit (4-byte) unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; @@ -96,13 +97,13 @@ class getid3_tiff } } - $ThisFileInfo['tiff']['ifd'][] = $CurrentIFD; + $info['tiff']['ifd'][] = $CurrentIFD; $CurrentIFD = array(); - $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); + $nextIFDoffset = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); } - foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) { + foreach ($info['tiff']['ifd'] as $IFDid => $IFDarray) { foreach ($IFDarray['fields'] as $key => $fieldarray) { switch ($fieldarray['raw']['tag']) { case 256: // ImageWidth @@ -110,8 +111,8 @@ class getid3_tiff case 258: // BitsPerSample case 259: // Compression if (!isset($fieldarray['value'])) { - fseek($fd, $fieldarray['offset'], SEEK_SET); - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); + fseek($this->getid3->fp, $fieldarray['offset'], SEEK_SET); + $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($this->getid3->fp, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); } break; @@ -124,36 +125,36 @@ class getid3_tiff case 315: // Artist case 316: // HostComputer if (isset($fieldarray['value'])) { - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; + $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; } else { - fseek($fd, $fieldarray['offset'], SEEK_SET); - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); + fseek($this->getid3->fp, $fieldarray['offset'], SEEK_SET); + $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($this->getid3->fp, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); } break; } switch ($fieldarray['raw']['tag']) { case 256: // ImageWidth - $ThisFileInfo['video']['resolution_x'] = $fieldarray['value']; + $info['video']['resolution_x'] = $fieldarray['value']; break; case 257: // ImageLength - $ThisFileInfo['video']['resolution_y'] = $fieldarray['value']; + $info['video']['resolution_y'] = $fieldarray['value']; break; case 258: // BitsPerSample if (isset($fieldarray['value'])) { - $ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value']; + $info['video']['bits_per_sample'] = $fieldarray['value']; } else { - $ThisFileInfo['video']['bits_per_sample'] = 0; + $info['video']['bits_per_sample'] = 0; for ($i = 0; $i < $fieldarray['raw']['length']; $i++) { - $ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']); + $info['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $info['tiff']['byte_order']); } } break; case 259: // Compression - $ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); + $info['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); break; case 270: // ImageDescription @@ -164,10 +165,10 @@ class getid3_tiff case 315: // Artist case 316: // HostComputer $TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']); - if (isset($ThisFileInfo['tiff']['comments'][$TIFFcommentName])) { - $ThisFileInfo['tiff']['comments'][$TIFFcommentName][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; + if (isset($info['tiff']['comments'][$TIFFcommentName])) { + $info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; } else { - $ThisFileInfo['tiff']['comments'][$TIFFcommentName] = array($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']); + $info['tiff']['comments'][$TIFFcommentName] = array($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']); } break; diff --git a/modules/getid3/module.misc.cue.php b/modules/getid3/module.misc.cue.php index 351915f9..c99ce247 100644 --- a/modules/getid3/module.misc.cue.php +++ b/modules/getid3/module.misc.cue.php @@ -30,14 +30,16 @@ * A CueSheet class used to open and parse cuesheets. * */ -class getid3_cue +class getid3_cue extends getid3_handler { var $cuesheet = array(); - function getid3_cue(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'cue'; - $this->readCueSheetFilename($ThisFileInfo['filenamepath']); - $ThisFileInfo['cue'] = $this->cuesheet; + function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'cue'; + $this->readCueSheetFilename($info['filenamepath']); + $info['cue'] = $this->cuesheet; return true; } diff --git a/modules/getid3/module.misc.exe.php b/modules/getid3/module.misc.exe.php index 8c6bfcf9..108e62ec 100644 --- a/modules/getid3/module.misc.exe.php +++ b/modules/getid3/module.misc.exe.php @@ -14,41 +14,43 @@ ///////////////////////////////////////////////////////////////// -class getid3_exe +class getid3_exe extends getid3_handler { - function getid3_exe(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $EXEheader = fread($fd, 28); + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $EXEheader = fread($this->getid3->fp, 28); - if (substr($EXEheader, 0, 2) != 'MZ') { - $ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.'; + $magic = 'MZ'; + if (substr($EXEheader, 0, 2) != $magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($EXEheader, 0, 2)).'"'; return false; } - $ThisFileInfo['fileformat'] = 'exe'; - $ThisFileInfo['exe']['mz']['magic'] = 'MZ'; - - $ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2)); - $ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2)); - $ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2)); - $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2)); - $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2)); - $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2)); - $ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2)); - $ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2)); - $ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2)); - $ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4)); - $ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2)); - $ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2)); - - $ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size']; - $ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16; - $ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16; - $ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16; - -$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()'; + $info['fileformat'] = 'exe'; + $info['exe']['mz']['magic'] = 'MZ'; + + $info['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2)); + $info['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2)); + $info['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2)); + $info['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2)); + $info['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2)); + $info['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2)); + $info['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2)); + $info['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2)); + $info['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2)); + $info['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4)); + $info['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2)); + $info['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2)); + + $info['exe']['mz']['byte_size'] = (($info['exe']['mz']['raw']['page_count'] - 1)) * 512 + $info['exe']['mz']['raw']['last_page_size']; + $info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16; + $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; + +$info['error'][] = 'EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } diff --git a/modules/getid3/module.misc.iso.php b/modules/getid3/module.misc.iso.php index 94df9294..727fdf87 100644 --- a/modules/getid3/module.misc.iso.php +++ b/modules/getid3/module.misc.iso.php @@ -14,25 +14,27 @@ ///////////////////////////////////////////////////////////////// -class getid3_iso +class getid3_iso extends getid3_handler { - function getid3_iso($fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'iso'; + function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'iso'; for ($i = 16; $i <= 19; $i++) { - fseek($fd, 2048 * $i, SEEK_SET); - $ISOheader = fread($fd, 2048); + fseek($this->getid3->fp, 2048 * $i, SEEK_SET); + $ISOheader = fread($this->getid3->fp, 2048); if (substr($ISOheader, 1, 5) == 'CD001') { switch (ord($ISOheader{0})) { case 1: - $ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i; - $this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo); + $info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i; + $this->ParsePrimaryVolumeDescriptor($ISOheader); break; case 2: - $ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i; - $this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo); + $info['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i; + $this->ParseSupplementaryVolumeDescriptor($ISOheader); break; default: @@ -42,35 +44,33 @@ class getid3_iso } } - $this->ParsePathTable($fd, $ThisFileInfo); - - $ThisFileInfo['iso']['files'] = array(); - foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) { - - $ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo); + $this->ParsePathTable(); + $info['iso']['files'] = array(); + foreach ($info['iso']['path_table']['directories'] as $directorynum => $directorydata) { + $info['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($directorydata); } return true; - } - function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { + 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 // shortcuts - $ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array(); - $thisfile_iso_primaryVD = &$ThisFileInfo['iso']['primary_volume_descriptor']; + $info = &$this->getid3->info; + $info['iso']['primary_volume_descriptor']['raw'] = array(); + $thisfile_iso_primaryVD = &$info['iso']['primary_volume_descriptor']; $thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw']; $thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); $thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') { - $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['iso']); + $info['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead'; + unset($info['fileformat']); + unset($info['iso']); return false; } @@ -121,29 +121,30 @@ class getid3_iso $thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']); $thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']); - if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; + if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $info['filesize']) { + $info['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)'; } return true; } - function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { + 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 // shortcuts - $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array(); - $thisfile_iso_supplementaryVD = &$ThisFileInfo['iso']['supplementary_volume_descriptor']; + $info = &$this->getid3->info; + $info['iso']['supplementary_volume_descriptor']['raw'] = array(); + $thisfile_iso_supplementaryVD = &$info['iso']['supplementary_volume_descriptor']; $thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw']; $thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); $thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') { - $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['iso']); + $info['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead'; + unset($info['fileformat']); + unset($info['iso']); return false; } @@ -199,62 +200,63 @@ class getid3_iso $thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']); $thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']); - if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; + if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $info['filesize']) { + $info['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)'; } return true; } - function ParsePathTable($fd, &$ThisFileInfo) { - if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { + 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'])) { return false; } - if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) { - $PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']; - $PathTableSize = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size']; + if (isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) { + $PathTableLocation = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']; + $PathTableSize = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_size']; $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode } else { - $PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location']; - $PathTableSize = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size']; + $PathTableLocation = $info['iso']['primary_volume_descriptor']['raw']['path_table_l_location']; + $PathTableSize = $info['iso']['primary_volume_descriptor']['raw']['path_table_size']; $TextEncoding = 'ISO-8859-1'; // Latin-1 } - if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$ThisFileInfo['filesize'].')'; + if (($PathTableLocation * 2048) > $info['filesize']) { + $info['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$info['filesize'].')'; return false; } - $ThisFileInfo['iso']['path_table']['offset'] = $PathTableLocation * 2048; - fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET); - $ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize); + $info['iso']['path_table']['offset'] = $PathTableLocation * 2048; + fseek($this->getid3->fp, $info['iso']['path_table']['offset'], SEEK_SET); + $info['iso']['path_table']['raw'] = fread($this->getid3->fp, $PathTableSize); $offset = 0; $pathcounter = 1; while ($offset < $PathTableSize) { // shortcut - $ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array(); - $thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter]; + $info['iso']['path_table']['directories'][$pathcounter] = array(); + $thisfile_iso_pathtable_directories_current = &$info['iso']['path_table']['directories'][$pathcounter]; - $thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); + $thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1)); $offset += 1; - $thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); + $thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1)); $offset += 1; - $thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4)); + $thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 4)); $offset += 4; - $thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2)); + $thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 2)); $offset += 2; - $thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']); + $thisfile_iso_pathtable_directories_current['name'] = substr($info['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']); $offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2); - $thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $thisfile_iso_pathtable_directories_current['name']); + $thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $info['encoding'], $thisfile_iso_pathtable_directories_current['name']); $thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048; if ($pathcounter == 1) { $thisfile_iso_pathtable_directories_current['full_path'] = '/'; } else { - $thisfile_iso_pathtable_directories_current['full_path'] = $ThisFileInfo['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/'; + $thisfile_iso_pathtable_directories_current['full_path'] = $info['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/'; } $FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path']; @@ -265,19 +267,20 @@ class getid3_iso } - function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) { - if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) { + function ParseDirectoryRecord($directorydata) { + $info = &$this->getid3->info; + if (isset($info['iso']['supplementary_volume_descriptor'])) { $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode } else { $TextEncoding = 'ISO-8859-1'; // Latin-1 } - fseek($fd, $directorydata['location_bytes'], SEEK_SET); - $DirectoryRecordData = fread($fd, 1); + fseek($this->getid3->fp, $directorydata['location_bytes'], SEEK_SET); + $DirectoryRecordData = fread($this->getid3->fp, 1); while (ord($DirectoryRecordData{0}) > 33) { - $DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1); + $DirectoryRecordData .= fread($this->getid3->fp, ord($DirectoryRecordData{0}) - 1); $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); @@ -291,7 +294,7 @@ class getid3_iso $ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1)); $ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']); - $ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $ThisDirectoryRecord['raw']['file_identifier']); + $ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $info['encoding'], $ThisDirectoryRecord['raw']['file_identifier']); $ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize']; $ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048; @@ -307,11 +310,11 @@ class getid3_iso $ThisDirectoryRecord['filename'] = $directorydata['full_path']; } else { $ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']); - $ThisFileInfo['iso']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize'])); + $info['iso']['files'] = getid3_lib::array_merge_clobber($info['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize'])); } $DirectoryRecord[] = $ThisDirectoryRecord; - $DirectoryRecordData = fread($fd, 1); + $DirectoryRecordData = fread($this->getid3->fp, 1); } return $DirectoryRecord; diff --git a/modules/getid3/module.misc.msoffice.php b/modules/getid3/module.misc.msoffice.php index d12f02d4..8fea1085 100644 --- a/modules/getid3/module.misc.msoffice.php +++ b/modules/getid3/module.misc.msoffice.php @@ -14,20 +14,22 @@ ///////////////////////////////////////////////////////////////// -class getid3_msoffice +class getid3_msoffice extends getid3_handler { - function getid3_msoffice(&$fd, &$ThisFileInfo) { - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $DOCFILEheader = fread($fd, 8); + function Analyze() { + $info = &$this->getid3->info; + + fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); + $DOCFILEheader = fread($this->getid3->fp, 8); $magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"; if (substr($DOCFILEheader, 0, 8) != $magic) { - $ThisFileInfo['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$ThisFileInfo['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.'; + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.'; return false; } - $ThisFileInfo['fileformat'] = 'msoffice'; + $info['fileformat'] = 'msoffice'; -$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() [v'.GETID3_VERSION.']'; +$info['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } diff --git a/modules/getid3/module.misc.par2.php b/modules/getid3/module.misc.par2.php index 9f0e2218..d8b0a388 100644 --- a/modules/getid3/module.misc.par2.php +++ b/modules/getid3/module.misc.par2.php @@ -14,14 +14,15 @@ ///////////////////////////////////////////////////////////////// -class getid3_par2 +class getid3_par2 extends getid3_handler { - function getid3_par2(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'par2'; + $info['fileformat'] = 'par2'; - $ThisFileInfo['error'][] = 'PAR2 parsing not enabled in this version of getID3()'; + $info['error'][] = 'PAR2 parsing not enabled in this version of getID3()'; return false; } diff --git a/modules/getid3/module.misc.pdf.php b/modules/getid3/module.misc.pdf.php index c6e3294b..38ed646a 100644 --- a/modules/getid3/module.misc.pdf.php +++ b/modules/getid3/module.misc.pdf.php @@ -14,14 +14,15 @@ ///////////////////////////////////////////////////////////////// -class getid3_pdf +class getid3_pdf extends getid3_handler { - function getid3_pdf(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - $ThisFileInfo['fileformat'] = 'pdf'; + $info['fileformat'] = 'pdf'; - $ThisFileInfo['error'][] = 'PDF parsing not enabled in this version of getID3()'; + $info['error'][] = 'PDF parsing not enabled in this version of getID3() ['.$this->getid3->version().']'; return false; } diff --git a/modules/getid3/module.tag.apetag.php b/modules/getid3/module.tag.apetag.php index aba0ea0e..6522475f 100644 --- a/modules/getid3/module.tag.apetag.php +++ b/modules/getid3/module.tag.apetag.php @@ -13,13 +13,16 @@ // /// ///////////////////////////////////////////////////////////////// -class getid3_apetag +class getid3_apetag extends getid3_handler { + var $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 + var $overrideendoffset = 0; - function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) { + function Analyze() { + $info = &$this->getid3->info; - if (!getid3_lib::intValueSupported($ThisFileInfo['filesize'])) { - $ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; return false; } @@ -27,69 +30,69 @@ class getid3_apetag $apetagheadersize = 32; $lyrics3tagsize = 10; - if ($overrideendoffset == 0) { + if ($this->overrideendoffset == 0) { - fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); - $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); + fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); + $APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found before ID3v1 - $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize; + $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize; //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found, no ID3v1 - $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize']; + $info['ape']['tag_offset_end'] = $info['filesize']; } } else { - fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET); - if (fread($fd, 8) == 'APETAGEX') { - $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset; + fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET); + if (fread($this->getid3->fp, 8) == 'APETAGEX') { + $info['ape']['tag_offset_end'] = $this->overrideendoffset; } } - if (!isset($ThisFileInfo['ape']['tag_offset_end'])) { + if (!isset($info['ape']['tag_offset_end'])) { // APE tag not found - unset($ThisFileInfo['ape']); + unset($info['ape']); return false; } // shortcut - $thisfile_ape = &$ThisFileInfo['ape']; + $thisfile_ape = &$info['ape']; - fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); - $APEfooterData = fread($fd, 32); + fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); + $APEfooterData = fread($this->getid3->fp, 32); if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { - $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; + $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; return false; } if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { - fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); - $thisfile_ape['tag_offset_start'] = ftell($fd); - $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); + fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); + $thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp); + $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); } else { $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; - fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET); - $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']); + fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET); + $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']); } - $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start']; + $info['avdataend'] = $thisfile_ape['tag_offset_start']; - if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { - $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; - unset($ThisFileInfo['id3v1']); - foreach ($ThisFileInfo['warning'] as $key => $value) { + if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { + $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; + unset($info['id3v1']); + foreach ($info['warning'] as $key => $value) { if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { - unset($ThisFileInfo['warning'][$key]); - sort($ThisFileInfo['warning']); + unset($info['warning'][$key]); + sort($info['warning']); break; } } @@ -100,14 +103,14 @@ class getid3_apetag if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { $offset += $apetagheadersize; } else { - $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; + $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; return false; } } // shortcut - $ThisFileInfo['replay_gain'] = array(); - $thisfile_replaygain = &$ThisFileInfo['replay_gain']; + $info['replay_gain'] = array(); + $thisfile_replaygain = &$info['replay_gain']; for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); @@ -115,7 +118,7 @@ class getid3_apetag $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; if (strstr(substr($APEtagData, $offset), "\x00") === false) { - $ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); + $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); return false; } $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; @@ -152,7 +155,7 @@ class getid3_apetag $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['track']['originator'] = 'unspecified'; if ($thisfile_replaygain['track']['peak'] <= 0) { - $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; } break; @@ -165,7 +168,7 @@ class getid3_apetag $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['album']['originator'] = 'unspecified'; if ($thisfile_replaygain['album']['peak'] <= 0) { - $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; } break; @@ -219,17 +222,55 @@ class getid3_apetag case 'cover art (studio)': // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2); - $thisfile_ape_items_current['dataoffset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); + $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); + $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); $thisfile_ape_items_current['image_mime'] = ''; $imageinfo = array(); $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); - if (!isset($ThisFileInfo['ape']['comments']['picture'])) { - $ThisFileInfo['ape']['comments']['picture'] = array(); - } - $ThisFileInfo['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); + do { + if ($this->inline_attachments === false) { + // skip entirely + unset($thisfile_ape_items_current['data']); + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)'; + unset($thisfile_ape_items_current['data']); + break; + } + } 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)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; + unset($thisfile_ape_items_current['data']); + break; + } + } + // 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)) { + file_put_contents($destination_filename, $thisfile_ape_items_current['data']); + } else { + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $thisfile_ape_items_current['data_filename'] = $destination_filename; + unset($thisfile_ape_items_current['data']); + } else { + if (!isset($info['ape']['comments']['picture'])) { + $info['ape']['comments']['picture'] = array(); + } + $info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); + } + } while (false); break; default: @@ -243,9 +284,8 @@ class getid3_apetag } if (empty($thisfile_replaygain)) { - unset($ThisFileInfo['replay_gain']); + unset($info['replay_gain']); } - return true; } diff --git a/modules/getid3/module.tag.id3v1.php b/modules/getid3/module.tag.id3v1.php index 30a40b72..ffde0b85 100644 --- a/modules/getid3/module.tag.id3v1.php +++ b/modules/getid3/module.tag.id3v1.php @@ -14,23 +14,24 @@ ///////////////////////////////////////////////////////////////// -class getid3_id3v1 +class getid3_id3v1 extends getid3_handler { - function getid3_id3v1(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; - if (!getid3_lib::intValueSupported($ThisFileInfo['filesize'])) { - $ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; return false; } - fseek($fd, -256, SEEK_END); - $preid3v1 = fread($fd, 128); - $id3v1tag = fread($fd, 128); + fseek($this->getid3->fp, -256, SEEK_END); + $preid3v1 = fread($this->getid3->fp, 128); + $id3v1tag = fread($this->getid3->fp, 128); if (substr($id3v1tag, 0, 3) == 'TAG') { - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128; + $info['avdataend'] = $info['filesize'] - 128; $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); @@ -71,13 +72,13 @@ class getid3_id3v1 $ParsedID3v1['padding_valid'] = true; if ($id3v1tag !== $GoodFormatID3v1tag) { $ParsedID3v1['padding_valid'] = false; - $ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; + $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; } - $ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize']; + $ParsedID3v1['tag_offset_end'] = $info['filesize']; $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; - $ThisFileInfo['id3v1'] = $ParsedID3v1; + $info['id3v1'] = $ParsedID3v1; } if (substr($preid3v1, 0, 3) == 'TAG') { @@ -93,8 +94,8 @@ class getid3_id3v1 // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch } else { // APE and Lyrics3 footers not found - assume double ID3v1 - $ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; - $ThisFileInfo['avdataend'] -= 128; + $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; + $info['avdataend'] -= 128; } } diff --git a/modules/getid3/module.tag.id3v2.php b/modules/getid3/module.tag.id3v2.php index 53553fed..17841510 100644 --- a/modules/getid3/module.tag.id3v2.php +++ b/modules/getid3/module.tag.id3v2.php @@ -15,10 +15,14 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); -class getid3_id3v2 +class getid3_id3v2 extends getid3_handler { + var $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 + var $StartingOffset = 0; + + function Analyze() { + $info = &$this->getid3->info; - function getid3_id3v2(&$fd, &$ThisFileInfo, $StartingOffset=0) { // Overall tag structure: // +-----------------------------+ // | Header (10 bytes) | @@ -42,14 +46,14 @@ class getid3_id3v2 // shortcuts - $ThisFileInfo['id3v2']['header'] = true; - $thisfile_id3v2 = &$ThisFileInfo['id3v2']; + $info['id3v2']['header'] = true; + $thisfile_id3v2 = &$info['id3v2']; $thisfile_id3v2['flags'] = array(); $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; - fseek($fd, $StartingOffset, SEEK_SET); - $header = fread($fd, 10); + fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET); + $header = fread($this->getid3->fp, 10); if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { $thisfile_id3v2['majorversion'] = ord($header{3}); @@ -60,14 +64,14 @@ class getid3_id3v2 } else { - unset($ThisFileInfo['id3v2']); + unset($info['id3v2']); return false; } if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) - $ThisFileInfo['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; + $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; return false; } @@ -98,7 +102,7 @@ class getid3_id3v2 $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length - $thisfile_id3v2['tag_offset_start'] = $StartingOffset; + $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset; $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; @@ -128,7 +132,7 @@ class getid3_id3v2 } if ($sizeofframes > 0) { - $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable + $framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { @@ -191,33 +195,56 @@ class getid3_id3v2 // d - Tag restrictions // Flag data length $01 - $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 1); + $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true); $extended_header_offset += 4; - $thisfile_id3v2['exthead']['flag_bytes'] = 1; + $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1 + $extended_header_offset += 1; + $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; - $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x4000); - $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x2000); - $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x1000); + $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40); + $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20); + $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10); + + if ($thisfile_id3v2['exthead']['flags']['update']) { + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0 + $extended_header_offset += 1; + } if ($thisfile_id3v2['exthead']['flags']['crc']) { - $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 5), 1); - $extended_header_offset += 5; + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5 + $extended_header_offset += 1; + $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false); + $extended_header_offset += $ext_header_chunk_length; } + if ($thisfile_id3v2['exthead']['flags']['restrictions']) { + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1 + $extended_header_offset += 1; + // %ppqrrstt $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); $extended_header_offset += 1; - $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw && 0xC0) >> 6; // p - Tag size restrictions - $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw && 0x20) >> 5; // q - Text encoding restrictions - $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw && 0x18) >> 3; // r - Text fields size restrictions - $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw && 0x04) >> 2; // s - Image encoding restrictions - $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw && 0x03) >> 0; // t - Image size restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions + + $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']); } + if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) { + $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')'; + } } + $framedataoffset += $extended_header_offset; $framedata = substr($framedata, $extended_header_offset); } // end extended header @@ -233,7 +260,7 @@ class getid3_id3v2 if ($framedata{$i} != "\x00") { $thisfile_id3v2['padding']['valid'] = false; $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; break; } } @@ -273,7 +300,7 @@ class getid3_id3v2 } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { // MP3ext known broken frames - "ok" for the purposes of this test } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { - $ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; + $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; $id3v2_majorversion = 3; $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } @@ -295,7 +322,7 @@ class getid3_id3v2 if ($framedata{$i} != "\x00") { $thisfile_id3v2['padding']['valid'] = false; $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; break; } } @@ -303,7 +330,7 @@ class getid3_id3v2 } if ($frame_name == 'COM ') { - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; $frame_name = 'COMM'; } if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { @@ -315,7 +342,7 @@ class getid3_id3v2 $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); $parsedFrame['dataoffset'] = $framedataoffset; - $this->ParseID3v2Frame($parsedFrame, $ThisFileInfo); + $this->ParseID3v2Frame($parsedFrame); $thisfile_id3v2[$frame_name][] = $parsedFrame; $framedata = substr($framedata, $frame_size); @@ -328,28 +355,28 @@ class getid3_id3v2 // next frame is valid, just skip the current frame $framedata = substr($framedata, $frame_size); - $ThisFileInfo['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; + $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; } else { // next frame is invalid too, abort processing //unset($framedata); $framedata = null; - $ThisFileInfo['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; + $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; } } elseif ($frame_size == strlen($framedata)) { // this is the last frame, just skip - $ThisFileInfo['warning'][] = 'This was the last ID3v2 frame.'; + $info['warning'][] = 'This was the last ID3v2 frame.'; } else { // next frame is invalid too, abort processing //unset($framedata); $framedata = null; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 frame size, aborting.'; + $info['warning'][] = 'Invalid ID3v2 frame size, aborting.'; } if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { @@ -362,21 +389,21 @@ class getid3_id3v2 case "\x00".'MP': case ' MP': case 'MP3': - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; break; default: - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; break; } } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) { - $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).'; + $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).'; } else { - $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; + $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; } @@ -397,7 +424,7 @@ class getid3_id3v2 // ID3v2 size 4 * %0xxxxxxx if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { - $footer = fread($fd, 10); + $footer = fread($this->getid3->fp, 10); if (substr($footer, 0, 3) == '3DI') { $thisfile_id3v2['footer'] = true; $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); @@ -435,9 +462,9 @@ class getid3_id3v2 // Set avdataoffset - $ThisFileInfo['avdataoffset'] = $thisfile_id3v2['headerlength']; + $info['avdataoffset'] = $thisfile_id3v2['headerlength']; if (isset($thisfile_id3v2['footer'])) { - $ThisFileInfo['avdataoffset'] += 10; + $info['avdataoffset'] += 10; } return true; @@ -467,10 +494,11 @@ class getid3_id3v2 } - function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo) { + function ParseID3v2Frame(&$parsedFrame) { // shortcuts - $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion']; + $info = &$this->getid3->info; + $id3v2_majorversion = $info['id3v2']['majorversion']; $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); if (empty($parsedFrame['framenamelong'])) { @@ -512,9 +540,6 @@ class getid3_id3v2 if ($parsedFrame['flags']['DataLengthIndicator']) { $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); $parsedFrame['data'] = substr($parsedFrame['data'], 4); - if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { - $ThisFileInfo['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data'; - } } } @@ -522,21 +547,25 @@ class getid3_id3v2 if ($parsedFrame['flags']['compression']) { $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); if (!function_exists('gzuncompress')) { - $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; } else { - ob_start(); - if ($decompresseddata = gzuncompress(substr($parsedFrame['data'], 4))) { - ob_end_clean(); + if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { + //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { $parsedFrame['data'] = $decompresseddata; + unset($decompresseddata); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; } } } } + if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { + if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { + $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data'; + } + } + if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; @@ -548,7 +577,7 @@ class getid3_id3v2 default: break; } - $ThisFileInfo['warning'][] = $warning; + $info['warning'][] = $warning; } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier @@ -574,7 +603,7 @@ class getid3_id3v2 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { @@ -590,7 +619,7 @@ class getid3_id3v2 $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data'])); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); } unset($parsedFrame['data']); @@ -605,7 +634,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); @@ -614,13 +643,9 @@ class getid3_id3v2 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - - // remove possible terminating \x00 (put by encoding id or software bug) - $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - if ($string[strlen($string) - 1] == "\x00") { - $string = substr($string, 0, strlen($string) - 1); - } - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; + $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); + $string = rtrim($string, "\x00"); // remove possible terminating null (put by encoding id or software bug) + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; unset($string); } @@ -636,7 +661,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { @@ -668,7 +693,7 @@ class getid3_id3v2 $parsedFrame['url'] = $frame_urldata; $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); } unset($parsedFrame['data']); @@ -682,7 +707,7 @@ class getid3_id3v2 $parsedFrame['url'] = trim($parsedFrame['data']); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; } unset($parsedFrame['data']); @@ -697,14 +722,14 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } @@ -715,7 +740,7 @@ class getid3_id3v2 // CD TOC <binary data> if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; } @@ -816,7 +841,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; @@ -838,7 +863,7 @@ class getid3_id3v2 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); $parsedFrame['description'] = $frame_description; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); @@ -862,7 +887,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; @@ -913,14 +938,14 @@ class getid3_id3v2 if (strlen($parsedFrame['data']) < 5) { - $ThisFileInfo['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; + $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; } else { $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; @@ -942,7 +967,7 @@ class getid3_id3v2 $parsedFrame['description'] = $frame_description; $parsedFrame['data'] = $frame_text; if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } } @@ -977,7 +1002,7 @@ class getid3_id3v2 $frame_offset += 2; $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) { - $ThisFileInfo['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'; + $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'; break; } $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); @@ -1185,7 +1210,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { @@ -1218,46 +1243,87 @@ class getid3_id3v2 $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - if ($id3v2_majorversion == 2) { - $parsedFrame['imagetype'] = $frame_imagetype; + if ($frame_offset >= $parsedFrame['datalength']) { + $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); } else { - $parsedFrame['mime'] = $frame_mimetype; - } - $parsedFrame['picturetypeid'] = $frame_picturetype; - $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); - $parsedFrame['description'] = $frame_description; - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - - $parsedFrame['image_mime'] = ''; - $imageinfo = array(); - $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); - if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { - $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); - if ($imagechunkcheck[0]) { - $parsedFrame['image_width'] = $imagechunkcheck[0]; + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } - if ($imagechunkcheck[1]) { - $parsedFrame['image_height'] = $imagechunkcheck[1]; + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; } - $parsedFrame['image_bytes'] = strlen($parsedFrame['data']); - } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - if (!isset($ThisFileInfo['id3v2']['comments']['picture'])) { - $ThisFileInfo['id3v2']['comments']['picture'] = array(); + if ($id3v2_majorversion == 2) { + $parsedFrame['imagetype'] = $frame_imagetype; + } else { + $parsedFrame['mime'] = $frame_mimetype; } - $ThisFileInfo['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); + $parsedFrame['picturetypeid'] = $frame_picturetype; + $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + $parsedFrame['datalength'] = strlen($parsedFrame['data']); + + $parsedFrame['image_mime'] = ''; + $imageinfo = array(); + $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); + if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); + if ($imagechunkcheck[0]) { + $parsedFrame['image_width'] = $imagechunkcheck[0]; + } + if ($imagechunkcheck[1]) { + $parsedFrame['image_height'] = $imagechunkcheck[1]; + } + } + + do { + if ($this->inline_attachments === false) { + // skip entirely + unset($parsedFrame['data']); + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $parsedFrame['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; + unset($parsedFrame['data']); + break; + } + } 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)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; + unset($parsedFrame['data']); + break; + } + } + // if we get this far, must be OK + if (is_string($this->inline_attachments)) { + $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $parsedFrame['data']); + } else { + $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $parsedFrame['data_filename'] = $destination_filename; + unset($parsedFrame['data']); + } else { + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + if (!isset($info['id3v2']['comments']['picture'])) { + $info['id3v2']['comments']['picture'] = array(); + } + $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); + } + } + } while (false); } } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object @@ -1274,7 +1340,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1325,7 +1391,7 @@ class getid3_id3v2 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter // There may be more than one 'POPM' frame in each tag, // but only one with the same email address // <Header for 'Popularimeter', ID: 'POPM'> @@ -1341,9 +1407,9 @@ class getid3_id3v2 } $frame_offset = $frame_terminatorpos + strlen("\x00"); $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); - $parsedFrame['email'] = $frame_emailaddress; - $parsedFrame['rating'] = $frame_rating; + $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); + $parsedFrame['email'] = $frame_emailaddress; + $parsedFrame['rating'] = $frame_rating; unset($parsedFrame['data']); @@ -1446,7 +1512,7 @@ class getid3_id3v2 $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); } unset($parsedFrame['data']); @@ -1474,7 +1540,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; @@ -1485,7 +1551,7 @@ class getid3_id3v2 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); } unset($parsedFrame['data']); @@ -1501,7 +1567,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); @@ -1541,7 +1607,7 @@ class getid3_id3v2 $frame_offset = 0; $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; } $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); @@ -1747,11 +1813,11 @@ class getid3_id3v2 $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); - $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; - $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; - $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; - $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; + $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; + $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; + $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; + $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; + $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; unset($parsedFrame['data']); @@ -1765,6 +1831,52 @@ class getid3_id3v2 return str_replace("\xFF\x00", "\xFF", $data); } + function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { + static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( + 0x00 => 'No more than 128 frames and 1 MB total tag size', + 0x01 => 'No more than 64 frames and 128 KB total tag size', + 0x02 => 'No more than 32 frames and 40 KB total tag size', + 0x03 => 'No more than 32 frames and 4 KB total tag size', + ); + return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); + } + + function LookupExtendedHeaderRestrictionsTextEncodings($index) { + static $LookupExtendedHeaderRestrictionsTextEncodings = array( + 0x00 => 'No restrictions', + 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', + ); + return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); + } + + function LookupExtendedHeaderRestrictionsTextFieldSize($index) { + static $LookupExtendedHeaderRestrictionsTextFieldSize = array( + 0x00 => 'No restrictions', + 0x01 => 'No string is longer than 1024 characters', + 0x02 => 'No string is longer than 128 characters', + 0x03 => 'No string is longer than 30 characters', + ); + return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); + } + + function LookupExtendedHeaderRestrictionsImageEncoding($index) { + static $LookupExtendedHeaderRestrictionsImageEncoding = array( + 0x00 => 'No restrictions', + 0x01 => 'Images are encoded only with PNG or JPEG', + ); + return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); + } + + function LookupExtendedHeaderRestrictionsImageSizeSize($index) { + static $LookupExtendedHeaderRestrictionsImageSizeSize = array( + 0x00 => 'No restrictions', + 0x01 => 'All images are 256x256 pixels or smaller', + 0x02 => 'All images are 64x64 pixels or smaller', + 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', + ); + return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); + } + function LookupCurrencyUnits($currencyid) { $begin = __LINE__; @@ -2931,8 +3043,8 @@ class getid3_id3v2 ASPI audio_seek_point_index BUF recommended_buffer_size CNT play_counter - COM comments - COMM comments + COM comment + COMM comment COMR commercial_frame CRA audio_encryption CRM encrypted_meta_frame @@ -3158,7 +3270,7 @@ class getid3_id3v2 if (strlen($datestamp) != 8) { return false; } - if (!$this->IsANumber($datestamp, false)) { + if (!self::IsANumber($datestamp, false)) { return false; } $year = substr($datestamp, 0, 4); diff --git a/modules/getid3/module.tag.lyrics3.php b/modules/getid3/module.tag.lyrics3.php index ccd25e55..aaff7178 100644 --- a/modules/getid3/module.tag.lyrics3.php +++ b/modules/getid3/module.tag.lyrics3.php @@ -14,19 +14,21 @@ ///////////////////////////////////////////////////////////////// -class getid3_lyrics3 +class getid3_lyrics3 extends getid3_handler { - function getid3_lyrics3(&$fd, &$ThisFileInfo) { + function Analyze() { + $info = &$this->getid3->info; + // http://www.volweb.cz/str/tags.htm - if (!getid3_lib::intValueSupported($ThisFileInfo['filesize'])) { - $ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; return false; } - fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] - $lyrics3_id3v1 = fread($fd, 128 + 9 + 6); + fseek($this->getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] + $lyrics3_id3v1 = fread($this->getid3->fp, 128 + 9 + 6); $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 @@ -35,7 +37,7 @@ class getid3_lyrics3 // Lyrics3v1, ID3v1, no APE $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; + $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; $lyrics3version = 1; } elseif ($lyrics3end == 'LYRICS200') { @@ -43,49 +45,49 @@ class getid3_lyrics3 // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); - $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; + $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; $lyrics3version = 2; } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) { // Lyrics3v1, no ID3v1, no APE $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + $lyrics3offset = $info['filesize'] - $lyrics3size; $lyrics3version = 1; - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + $lyrics3offset = $info['filesize'] - $lyrics3size; } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) { // 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' - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + $lyrics3offset = $info['filesize'] - $lyrics3size; $lyrics3version = 2; } else { - if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) { + if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) { - fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET); - $lyrics3lsz = fread($fd, 6); - $lyrics3end = fread($fd, 9); + fseek($this->getid3->fp, $info['ape']['tag_offset_start'] - 15, SEEK_SET); + $lyrics3lsz = fread($this->getid3->fp, 6); + $lyrics3end = fread($this->getid3->fp, 9); if ($lyrics3end == 'LYRICSEND') { // Lyrics3v1, APE, maybe ID3v1 $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; - $ThisFileInfo['avdataend'] = $lyrics3offset; + $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; + $info['avdataend'] = $lyrics3offset; $lyrics3version = 1; - $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; } elseif ($lyrics3end == 'LYRICS200') { // Lyrics3v2, APE, maybe ID3v1 $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' - $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; + $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; $lyrics3version = 2; - $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; } @@ -94,14 +96,24 @@ class getid3_lyrics3 } if (isset($lyrics3offset)) { - $ThisFileInfo['avdataend'] = $lyrics3offset; - $this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size); + $info['avdataend'] = $lyrics3offset; + $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); - if (!isset($ThisFileInfo['ape'])) { - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (!isset($info['ape'])) { + $GETID3_ERRORARRAY = &$info['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) { - $tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']); - unset($tag); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_apetag = new getid3_apetag($getid3_temp); + $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; + $getid3_apetag->Analyze(); + if (!empty($getid3_temp->info['ape'])) { + $info['ape'] = $getid3_temp->info['ape']; + } + if (!empty($getid3_temp->info['replay_gain'])) { + $info['replay_gain'] = $getid3_temp->info['replay_gain']; + } + unset($getid3_temp, $getid3_apetag); } } @@ -110,43 +122,46 @@ class getid3_lyrics3 return true; } - function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) { + function getLyrics3Data($endoffset, $version, $length) { // http://www.volweb.cz/str/tags.htm + $info = &$this->getid3->info; + if (!getid3_lib::intValueSupported($endoffset)) { - $ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; return false; } - fseek($fd, $endoffset, SEEK_SET); + fseek($this->getid3->fp, $endoffset, SEEK_SET); if ($length <= 0) { return false; } - $rawdata = fread($fd, $length); + $rawdata = fread($this->getid3->fp, $length); + + $ParsedLyrics3['raw']['lyrics3version'] = $version; + $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; + $ParsedLyrics3['tag_offset_start'] = $endoffset; + $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1; if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') { if (strpos($rawdata, 'LYRICSBEGIN') !== false) { - $ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; - $ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); - $ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend']; + $info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; + $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN')); $length = strlen($rawdata); + $ParsedLyrics3['tag_offset_start'] = $info['avdataend']; + $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; } else { - $ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; + $info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; return false; } } - $ParsedLyrics3['raw']['lyrics3version'] = $version; - $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; - $ParsedLyrics3['tag_offset_start'] = $endoffset; - $ParsedLyrics3['tag_offset_end'] = $endoffset + $length; - switch ($version) { case 1: @@ -154,7 +169,7 @@ class getid3_lyrics3 $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); } else { - $ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + $info['error'][] = '"LYRICSEND" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; return false; } break; @@ -202,31 +217,31 @@ class getid3_lyrics3 $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); } } else { - $ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + $info['error'][] = '"LYRICS200" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; return false; } break; default: - $ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; + $info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; return false; break; } - if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) { - $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; - unset($ThisFileInfo['id3v1']); - foreach ($ThisFileInfo['warning'] as $key => $value) { + if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) { + $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; + unset($info['id3v1']); + foreach ($info['warning'] as $key => $value) { if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { - unset($ThisFileInfo['warning'][$key]); - sort($ThisFileInfo['warning']); + unset($info['warning'][$key]); + sort($info['warning']); break; } } } - $ThisFileInfo['lyrics3'] = $ParsedLyrics3; + $info['lyrics3'] = $ParsedLyrics3; return true; } diff --git a/modules/getid3/module.tag.xmp.php b/modules/getid3/module.tag.xmp.php index 7354f082..141fd09d 100644 --- a/modules/getid3/module.tag.xmp.php +++ b/modules/getid3/module.tag.xmp.php @@ -91,16 +91,9 @@ class Image_XMP // Attempt to open the jpeg file - the at symbol supresses the error message about // not being able to open files. The file_exists would have been used, but it // does not work with files fetched over http or ftp. - ob_start(); - $filehnd = fopen($filename, 'rb'); - $errormessage = ob_get_contents(); - ob_end_clean(); - - // Check if the file opened successfully - if (!$filehnd) - { - // Could't open the file - exit - echo '<p>Could not open file '.htmlentities($filename).'</p>'."\n"; + if (is_readable($filename) && is_file($filename) && ($filehnd = fopen($filename, 'rb'))) { + // great + } else { return false; } @@ -218,7 +211,7 @@ class Image_XMP // Return the XMP text $xmp_data = substr($jpeg_header_data[$i]['SegData'], 29); - return $xmp_data; + 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) } } } @@ -333,14 +326,14 @@ class Image_XMP if (array_key_exists('attributes', $xml_elem)) { // If Lang Alt (language alternatives) then ensure we take the default language - if ($xml_elem['attributes']['xml:lang'] != 'x-default') + if (isset($xml_elem['attributes']['xml:lang']) && ($xml_elem['attributes']['xml:lang'] != 'x-default')) { break; } } if ($current_property != '') { - $xmp_array[$current_property][$container_index] = $xml_elem['value']; + $xmp_array[$current_property][$container_index] = (isset($xml_elem['value']) ? $xml_elem['value'] : ''); $container_index += 1; } //else unidentified attribute!! diff --git a/modules/getid3/write.apetag.php b/modules/getid3/write.apetag.php index 8b157181..2b553699 100644 --- a/modules/getid3/write.apetag.php +++ b/modules/getid3/write.apetag.php @@ -21,9 +21,9 @@ class getid3_write_apetag var $filename; var $tag_data; - var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here + var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here function getid3_write_apetag() { return true; @@ -56,9 +56,7 @@ class getid3_write_apetag } if ($APEtag = $this->GenerateAPEtag()) { - ob_start(); - if ($fp = fopen($this->filename, 'a+b')) { - ob_end_clean(); + if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { $oldignoreuserabort = ignore_user_abort(true); flock($fp, LOCK_EX); @@ -89,9 +87,6 @@ class getid3_write_apetag ignore_user_abort($oldignoreuserabort); return true; } - $errormessage = ob_get_contents(); - ob_end_clean(); - return false; } return false; } @@ -100,9 +95,7 @@ class getid3_write_apetag $getID3 = new getID3; $ThisFileInfo = $getID3->analyze($this->filename); if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { - ob_start(); - if ($fp = fopen($this->filename, 'a+b')) { - ob_end_clean(); + if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { flock($fp, LOCK_EX); $oldignoreuserabort = ignore_user_abort(true); @@ -126,8 +119,6 @@ class getid3_write_apetag return true; } - $errormessage = ob_get_contents(); - ob_end_clean(); return false; } return true; diff --git a/modules/getid3/write.id3v1.php b/modules/getid3/write.id3v1.php index 28fc1606..cecccd8a 100644 --- a/modules/getid3/write.id3v1.php +++ b/modules/getid3/write.id3v1.php @@ -29,16 +29,13 @@ class getid3_write_id3v1 function WriteID3v1() { // File MUST be writeable - CHMOD(646) at least - if (!empty($this->filename) && is_writeable($this->filename)) { + if (!empty($this->filename) && is_readable($this->filename) && 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'; return false; } - ob_start(); if ($fp_source = fopen($this->filename, 'r+b')) { - - ob_end_clean(); fseek($fp_source, -128, SEEK_END); if (fread($fp_source, 3) == 'TAG') { fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag @@ -60,9 +57,7 @@ class getid3_write_id3v1 return true; } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")'; return false; } } @@ -94,16 +89,14 @@ class getid3_write_id3v1 function RemoveID3v1() { // File MUST be writeable - CHMOD(646) at least - if (!empty($this->filename) && is_writeable($this->filename)) { + if (!empty($this->filename) && is_readable($this->filename) && 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'; return false; } - ob_start(); if ($fp_source = fopen($this->filename, 'r+b')) { - ob_end_clean(); fseek($fp_source, -128, SEEK_END); if (fread($fp_source, 3) == 'TAG') { ftruncate($fp_source, $this->filesize - 128); @@ -114,9 +107,7 @@ class getid3_write_id3v1 return true; } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")'; } } else { $this->errors[] = $this->filename.' is not writeable'; diff --git a/modules/getid3/write.id3v2.php b/modules/getid3/write.id3v2.php index 9960f1ce..78333be9 100644 --- a/modules/getid3/write.id3v2.php +++ b/modules/getid3/write.id3v2.php @@ -19,6 +19,7 @@ class getid3_write_id3v2 { var $filename; var $tag_data; + var $fread_buffer_size = 32768; // read buffer size in bytes var $paddedlength = 4096; // minimum length of ID3v2 tag in bytes var $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4) var $minorversion = 0; // ID3v2 minor version - always 0 @@ -60,36 +61,31 @@ class getid3_write_id3v2 // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary) if (file_exists($this->filename)) { - ob_start(); - if ($fp = fopen($this->filename, 'r+b')) { + if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'r+b'))) { rewind($fp); fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); fclose($fp); } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents()); + $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")'; } - ob_end_clean(); } else { - ob_start(); - if ($fp = fopen($this->filename, 'wb')) { + if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'wb'))) { rewind($fp); fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); fclose($fp); } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents()); + $this->errors[] = 'Could not fopen("'.$this->filename.'", "wb")'; } - ob_end_clean(); } } else { if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - ob_start(); - if ($fp_source = fopen($this->filename, 'rb')) { - if ($fp_temp = fopen($tempfilename, 'wb')) { + 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'))) { fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag)); @@ -98,7 +94,7 @@ class getid3_write_id3v2 fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); } - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + while ($buffer = fread($fp_source, $this->fread_buffer_size)) { fwrite($fp_temp, $buffer, strlen($buffer)); } @@ -106,22 +102,16 @@ class getid3_write_id3v2 fclose($fp_source); copy($tempfilename, $this->filename); unlink($tempfilename); - ob_end_clean(); return true; } else { - - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); - + $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")'; } fclose($fp_source); } else { - - $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents()); - + $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")'; } - ob_end_clean(); } return false; @@ -150,9 +140,8 @@ class getid3_write_id3v2 // preferred method - only one copying operation, minimal chance of corrupting // original file if script is interrupted, but required directory to be writeable - ob_start(); - if ($fp_source = fopen($this->filename, 'rb')) { - ob_end_clean(); + if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) { + // Initialize getID3 engine $getID3 = new getID3; $OldThisFileInfo = $getID3->analyze($this->filename); @@ -165,23 +154,17 @@ class getid3_write_id3v2 if ($OldThisFileInfo['avdataoffset'] !== false) { fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); } - ob_start(); - if ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b')) { - ob_end_clean(); - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + if (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)); } fclose($fp_temp); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"'; + $this->errors[] = 'Could not fopen("'.$this->filename.'getid3tmp", "w+b")'; } fclose($fp_source); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; + $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")'; } if (file_exists($this->filename)) { unlink($this->filename); @@ -192,9 +175,8 @@ class getid3_write_id3v2 // 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. - ob_start(); - if ($fp_source = fopen($this->filename, 'rb')) { - ob_end_clean(); + if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) { + // Initialize getID3 engine $getID3 = new getID3; $OldThisFileInfo = $getID3->analyze($this->filename); @@ -208,32 +190,26 @@ class getid3_write_id3v2 fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); } if ($fp_temp = tmpfile()) { - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + while ($buffer = fread($fp_source, $this->fread_buffer_size)) { fwrite($fp_temp, $buffer, strlen($buffer)); } fclose($fp_source); - ob_start(); - if ($fp_source = fopen($this->filename, 'wb')) { - ob_end_clean(); + if (is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'wb'))) { rewind($fp_temp); - while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) { + while ($buffer = fread($fp_temp, $this->fread_buffer_size)) { fwrite($fp_source, $buffer, strlen($buffer)); } fseek($fp_temp, -128, SEEK_END); fclose($fp_source); } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "wb"'; + $this->errors[] = 'Could not fopen("'.$this->filename.'", "wb")'; } fclose($fp_temp); } else { $this->errors[] = 'Could not create tmpfile()'; } } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; + $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")'; } } else { @@ -1876,10 +1852,7 @@ class getid3_write_id3v2 } function safe_parse_url($url) { - ob_start(); - $parts = parse_url($url); - $errormessage = ob_get_contents(); - ob_end_clean(); + $parts = @parse_url($url); $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); $parts['host'] = (isset($parts['host']) ? $parts['host'] : ''); $parts['user'] = (isset($parts['user']) ? $parts['user'] : ''); @@ -1903,7 +1876,7 @@ class getid3_write_id3v2 if ($parts = $this->safe_parse_url($url)) { if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) { return false; - } elseif (!preg_match("#^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}#i$", $parts['host'], $regs) && !preg_match('#^[0-9]{1,3}(\.[0-9]{1,3}){3}$#', $parts['host'])) { + } elseif (!preg_match("#^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$#i", $parts['host'], $regs) && !preg_match('#^[0-9]{1,3}(\.[0-9]{1,3}){3}$#', $parts['host'])) { return false; } elseif (!preg_match("#^([[:alnum:]-]|[\_])*$#i", $parts['user'], $regs)) { return false; @@ -1911,7 +1884,7 @@ class getid3_write_id3v2 return false; } elseif (!preg_match("#^[[:alnum:]/_\.@~-]*$#i", $parts['path'], $regs)) { return false; - } elseif (!preg_match("#^[[:alnum:]?&=+:;_()%#/,\.-]*$#i", $parts['query'], $regs)) { + } elseif (!empty($parts['query']) && !preg_match("#^[[:alnum:]?&=+:;_()%#/,\.-]*$#i", $parts['query'], $regs)) { return false; } else { return true; diff --git a/modules/getid3/write.lyrics3.php b/modules/getid3/write.lyrics3.php index feb3face..fa49cd16 100644 --- a/modules/getid3/write.lyrics3.php +++ b/modules/getid3/write.lyrics3.php @@ -35,9 +35,7 @@ class getid3_write_lyrics3 $getID3 = new getID3; $ThisFileInfo = $getID3->analyze($this->filename); if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { - ob_start(); - if ($fp = fopen($this->filename, 'a+b')) { - ob_end_clean(); + if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { flock($fp, LOCK_EX); $oldignoreuserabort = ignore_user_abort(true); @@ -62,12 +60,8 @@ class getid3_write_lyrics3 return true; } else { - - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode'; + $this->errors[] = 'Cannot fopen('.$this->filename.', "a+b")'; return false; - } } // no Lyrics3 present diff --git a/modules/getid3/write.metaflac.php b/modules/getid3/write.metaflac.php index acf1035e..dfd6950a 100644 --- a/modules/getid3/write.metaflac.php +++ b/modules/getid3/write.metaflac.php @@ -35,9 +35,7 @@ class getid3_write_metaflac // Create file with new comments $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); - ob_start(); - if ($fpcomments = fopen($tempcommentsfilename, 'wb')) { - ob_end_clean(); + if (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"); @@ -46,12 +44,8 @@ class getid3_write_metaflac fclose($fpcomments); } else { - - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; + $this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$tempcommentsfilename.'", "wb")'; return false; - } $oldignoreuserabort = ignore_user_abort(true); diff --git a/modules/getid3/write.real.php b/modules/getid3/write.real.php index a64368e9..675a65e6 100644 --- a/modules/getid3/write.real.php +++ b/modules/getid3/write.real.php @@ -16,10 +16,11 @@ class getid3_write_real { var $filename; - var $tag_data = array(); - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - var $paddedlength = 512; // minimum length of CONT tag in bytes + var $tag_data = array(); + var $fread_buffer_size = 32768; // read buffer size in bytes + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + var $paddedlength = 512; // minimum length of CONT tag in bytes function getid3_write_real() { return true; @@ -27,115 +28,101 @@ class getid3_write_real function WriteReal() { // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - ob_start(); - if ($fp_source = fopen($this->filename, 'r+b')) { - - ob_end_clean(); - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { - $this->errors[] = 'Cannot write Real tags on old-style file format'; - fclose($fp_source); - return false; - } + if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { - if (empty($OldThisFileInfo['real']['chunks'])) { - $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file'; - fclose($fp_source); - return false; - } - foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { - $oldChunkInfo[$chunkarray['name']] = $chunkarray; - } - if (!empty($oldChunkInfo['CONT']['length'])) { - $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength); - } + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { + $this->errors[] = 'Cannot write Real tags on old-style file format'; + fclose($fp_source); + return false; + } - $new_CONT_tag_data = $this->GenerateCONTchunk(); - $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data); - $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } + if (!empty($oldChunkInfo['CONT']['length'])) { + $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength); + } - if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) { - fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); - fwrite($fp_source, $new__RMF_tag_data); - } else { - $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; - fclose($fp_source); - return false; - } + $new_CONT_tag_data = $this->GenerateCONTchunk(); + $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data); + $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); - if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) { - fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); - fwrite($fp_source, $new_PROP_tag_data); - } else { - $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; - fclose($fp_source); - return false; - } + if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) { + fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); + fwrite($fp_source, $new__RMF_tag_data); + } else { + $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; + fclose($fp_source); + return false; + } - if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) { + if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) { + fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); + fwrite($fp_source, $new_PROP_tag_data); + } else { + $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; + fclose($fp_source); + return false; + } - // new data length is same as old data length - just overwrite - fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); - fwrite($fp_source, $new_CONT_tag_data); - fclose($fp_source); - return true; + if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) { - } else { + // new data length is same as old data length - just overwrite + fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); + fwrite($fp_source, $new_CONT_tag_data); + fclose($fp_source); + return true; - if (empty($oldChunkInfo['CONT'])) { - // no existing CONT chunk - $BeforeOffset = $oldChunkInfo['DATA']['offset']; - $AfterOffset = $oldChunkInfo['DATA']['offset']; - } else { - // new data is longer than old data - $BeforeOffset = $oldChunkInfo['CONT']['offset']; - $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; - } - if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - ob_start(); - if ($fp_temp = fopen($tempfilename, 'wb')) { - - rewind($fp_source); - fwrite($fp_temp, fread($fp_source, $BeforeOffset)); - fwrite($fp_temp, $new_CONT_tag_data); - fseek($fp_source, $AfterOffset, SEEK_SET); - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_temp); - - if (copy($tempfilename, $this->filename)) { - unlink($tempfilename); - fclose($fp_source); - return true; - } - unlink($tempfilename); - $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + } else { - } else { + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + $BeforeOffset = $oldChunkInfo['DATA']['offset']; + $AfterOffset = $oldChunkInfo['DATA']['offset']; + } else { + // new data is longer than old data + $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'))) { - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fwrite($fp_temp, $new_CONT_tag_data); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, $this->fread_buffer_size)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; } - ob_end_clean(); - } - fclose($fp_source); - return false; + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')'; + } else { + $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")'; + } } - - - } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + fclose($fp_source); return false; + } + } - $this->errors[] = 'File is not writeable: '.$this->filename; + $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")'; return false; } @@ -225,76 +212,61 @@ class getid3_write_real function RemoveReal() { // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - ob_start(); - if ($fp_source = fopen($this->filename, 'r+b')) { - - ob_end_clean(); - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { - $this->errors[] = 'Cannot remove Real tags from old-style file format'; - fclose($fp_source); - return false; - } - - if (empty($OldThisFileInfo['real']['chunks'])) { - $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file'; - fclose($fp_source); - return false; - } - foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { - $oldChunkInfo[$chunkarray['name']] = $chunkarray; - } - - if (empty($oldChunkInfo['CONT'])) { - // no existing CONT chunk - fclose($fp_source); - return true; - } + if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b')) { - $BeforeOffset = $oldChunkInfo['CONT']['offset']; - $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; - if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) { - ob_start(); - if ($fp_temp = fopen($tempfilename, 'wb')) { - - rewind($fp_source); - fwrite($fp_temp, fread($fp_source, $BeforeOffset)); - fseek($fp_source, $AfterOffset, SEEK_SET); - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_temp); + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { + $this->errors[] = 'Cannot remove Real tags from old-style file format'; + fclose($fp_source); + return false; + } - if (copy($tempfilename, $this->filename)) { - unlink($tempfilename); - fclose($fp_source); - return true; - } - unlink($tempfilename); - $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } - } else { + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + fclose($fp_source); + return true; + } - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + $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'))) { + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, $this->fread_buffer_size)) { + fwrite($fp_temp, $buffer, strlen($buffer)); } - ob_end_clean(); - } - fclose($fp_source); - return false; + fclose($fp_temp); + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; + } + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')'; - } else { - $errormessage = ob_get_contents(); - ob_end_clean(); - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; - return false; + } else { + $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")'; + } } + fclose($fp_source); + return false; } - $this->errors[] = 'File is not writeable: '.$this->filename; + $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")'; return false; } diff --git a/modules/getid3/write.vorbiscomment.php b/modules/getid3/write.vorbiscomment.php index 03349249..ac8dc693 100644 --- a/modules/getid3/write.vorbiscomment.php +++ b/modules/getid3/write.vorbiscomment.php @@ -35,10 +35,8 @@ class getid3_write_vorbiscomment // Create file with new comments $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); - ob_start(); - if ($fpcomments = fopen($tempcommentsfilename, 'wb')) { + if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { - ob_end_clean(); foreach ($this->tag_data as $key => $value) { foreach ($value as $commentdata) { fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n"); @@ -47,12 +45,8 @@ class getid3_write_vorbiscomment fclose($fpcomments); } else { - - $errormessage = ob_get_contents(); - ob_end_clean(); $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; return false; - } $oldignoreuserabort = ignore_user_abort(true); |