1
0
Fork 0
mirror of https://github.com/Yetangitu/ampache synced 2025-10-05 10:49:37 +02:00

Update getID3 to 1.9.1

This commit is contained in:
Paul Arthur 2011-10-27 20:41:26 -04:00
parent 8e89cb990a
commit 51893b1ee5
72 changed files with 6361 additions and 4590 deletions

View file

@ -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)

View file

@ -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

View file

@ -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
* http://wyday.com/cuesharp/specification.php
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html

View file

@ -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'));

View file

@ -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);
}

View file

@ -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,32 +602,42 @@ 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;
}
fclose($fp_data);
$result = $hash_function($data_filename);
} else {
$errormessage = ob_get_contents();
ob_end_clean();
}
fclose($fp);
} else {
$errormessage = ob_get_contents();
ob_end_clean();
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_dest);
} else {
throw new Exception('failed to open file for reading '.$this->info['filenamepath']);
}
fclose($fp_src);
} else {
throw new Exception('failed to create file for writing '.$dest);
}
return false;
}
static function iconv_fallback_int_utf8($charval) {
if ($charval < 128) {
@ -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;
}

View file

@ -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)
// private
var $filename = '';
var $startup_error = '';
var $startup_warning = '';
// Public variables
public $filename; // Filename of file being analysed.
public $fp; // Filepointer to file being analysed.
public $info; // Result array.
// 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 {
$this->info['error'] = array('Caught exception: '.$e->getMessage());
}
}
}
fseek($fp, 0, SEEK_SET);
$header = fread($fp, 10);
// 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;
}
}
}
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']);
}
}
// 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['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['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
}
}
// 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.');
}
$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.');
}
$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.');
}
$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('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', 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);
}
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
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'];
$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']++;
$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

File diff suppressed because it is too large Load diff

View file

@ -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;
}
}

View file

@ -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) //
// //
// * 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;
}

File diff suppressed because it is too large Load diff

View file

@ -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.';
}
}

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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'];
$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'];
$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']);
$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 ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) {
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'];
}
}

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
// 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));
// 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') {
$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 ($framenumber == 0) {
$info['aac']['header_type'] = 'ADTS';
$info['fileformat'] = 'aac';
$info['audio']['dataformat'] = 'aac';
if (!$AACheaderBitstream[$bitoffset]) {
$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;
// MPEG-4
$bitoffset += 20;
} else {
// MPEG-2
$bitoffset += 18;
}
} 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;
}
$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';
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;
}
$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) {
$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['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'];
}
if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
$info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead';
}
if ($info['aac']['header']['sample_frequency'] == 0) {
$info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
return false;
}
$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);
getid3_lib::safe_inc($info['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';
$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++;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
$info['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));
$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));
$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);
$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);
$ThisFileInfo['avr']['midi_notes'] = array();
if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8;
$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;

View file

@ -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'];
$info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
$info['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';
$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';
$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['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) {

View file

@ -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;
}

View file

@ -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;
$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;
$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']);
$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']);
$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['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;

View file

@ -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;
}

View file

@ -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'];
} else {
$ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']);
}
unset($riff);
unset($dummy);
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 {
$info['warning'][] = 'Error parsing RIFF portion of La file: '.implode($getid3_temp->info['error']);
}
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;
}

View file

@ -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);
$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 ($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'].'"';
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'];
$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);
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
$info['audio']['channels'] = ($info['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;
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;
}

View file

@ -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'])) {

View file

@ -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;
}

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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
$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
$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['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;
}

View file

@ -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));
}

View file

@ -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;

View file

@ -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';
$info['fileformat'] = 'shn';
$info['audio']['dataformat'] = 'shn';
$info['audio']['lossless'] = true;
$info['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
$info['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') {
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) {
$ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"';
$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;
}

View file

@ -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;
$info['tta']['major_version'] = 2;
$info['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));
$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));
$ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
$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;
$info['tta']['major_version'] = 3;
$info['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));
$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));
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
$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;
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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));
$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));
$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);
$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);
$ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'];
$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';
}

View file

@ -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;
}
}

View file

@ -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;
}
}
?>

View file

@ -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);

View file

@ -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;

View file

@ -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;
$info['fileformat'] = 'pcd';
$info['video']['dataformat'] = 'pcd';
$info['video']['lossless'] = false;
fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET);
fseek($this->getid3->fp, $info['avdataoffset'] + 72, SEEK_SET);
$PCDflags = fread($fd, 1);
$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;

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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';
$info['fileformat'] = 'exe';
$info['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));
$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));
$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;
$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;
$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()';
$info['error'][] = 'EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
return false;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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)));
$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);
$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 ($id3v2_majorversion == 2) {
$parsedFrame['imagetype'] = $frame_imagetype;
} else {
$parsedFrame['mime'] = $frame_mimetype;
}
if ($imagechunkcheck[1]) {
$parsedFrame['image_height'] = $imagechunkcheck[1];
}
$parsedFrame['image_bytes'] = strlen($parsedFrame['data']);
}
$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']);
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
if (!isset($ThisFileInfo['id3v2']['comments']['picture'])) {
$ThisFileInfo['id3v2']['comments']['picture'] = array();
$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];
}
}
$ThisFileInfo['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']);
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);

View file

@ -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;
}

View file

@ -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!!

View file

@ -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;

View file

@ -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';

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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')) {
if (is_writeable($this->filename) && is_file($this->filename) && ($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 (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);
}
$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['.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['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['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
// 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;
} 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')) {
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 {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
ob_end_clean();
}
fclose($fp_source);
return false;
}
} else {
$errormessage = ob_get_contents();
ob_end_clean();
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
// 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 (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);
}
$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['.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['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['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
// 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;
} 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'))) {
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;
}
unlink($tempfilename);
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
} 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;
}
@ -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')) {
if (is_writeable($this->filename) && is_file($this->filename) && ($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;
}
$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);
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 {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
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;
}
} else {
$errormessage = ob_get_contents();
ob_end_clean();
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
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;
}
$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));
}
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 {
$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;
}

View file

@ -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);