fix merge conflicts
This commit is contained in:
commit
afcb0aee40
|
@ -77,35 +77,24 @@ class getID3_cached_dbm extends getID3
|
|||
|
||||
// Check for dba extension
|
||||
if (!extension_loaded('dba')) {
|
||||
die('PHP is not compiled with dba support, required to use DBM style cache.');
|
||||
throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.');
|
||||
}
|
||||
|
||||
// Check for specific dba driver
|
||||
if (function_exists('dba_handlers')) { // PHP 4.3.0+
|
||||
if (!in_array('db3', dba_handlers())) {
|
||||
die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
||||
}
|
||||
}
|
||||
else { // PHP <= 4.2.3
|
||||
ob_start(); // nasty, buy the only way to check...
|
||||
phpinfo();
|
||||
$contents = ob_get_contents();
|
||||
@ob_end_clean();
|
||||
if (!strstr($contents, $cache_type)) {
|
||||
die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
||||
}
|
||||
if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) {
|
||||
throw new Exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
||||
}
|
||||
|
||||
// Create lock file if needed
|
||||
if (!file_exists($lock_filename)) {
|
||||
if (!touch($lock_filename)) {
|
||||
die('failed to create lock file: ' . $lock_filename);
|
||||
throw new Exception('failed to create lock file: '.$lock_filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Open lock file for writing
|
||||
if (!is_writeable($lock_filename)) {
|
||||
die('lock file: ' . $lock_filename . ' is not writable');
|
||||
throw new Exception('lock file: '.$lock_filename.' is not writable');
|
||||
}
|
||||
$this->lock = fopen($lock_filename, 'w');
|
||||
|
||||
|
@ -115,23 +104,23 @@ class getID3_cached_dbm extends getID3
|
|||
// Create dbm-file if needed
|
||||
if (!file_exists($dbm_filename)) {
|
||||
if (!touch($dbm_filename)) {
|
||||
die('failed to create dbm file: ' . $dbm_filename);
|
||||
throw new Exception('failed to create dbm file: '.$dbm_filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open dbm file for writing
|
||||
$this->dba = @dba_open($dbm_filename, 'w', $cache_type);
|
||||
$this->dba = dba_open($dbm_filename, 'w', $cache_type);
|
||||
if (!$this->dba) {
|
||||
|
||||
// Failed - create new dbm file
|
||||
$this->dba = dba_open($dbm_filename, 'n', $cache_type);
|
||||
|
||||
if (!$this->dba) {
|
||||
die('failed to create dbm file: ' . $dbm_filename);
|
||||
throw new Exception('failed to create dbm file: '.$dbm_filename);
|
||||
}
|
||||
|
||||
// Insert getID3 version number
|
||||
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
|
||||
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
||||
}
|
||||
|
||||
// Init misc values
|
||||
|
@ -142,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(getID3::VERSION, $this->dba) != getID3::VERSION) {
|
||||
$this->clear_cache();
|
||||
}
|
||||
|
||||
|
@ -155,13 +144,13 @@ class getID3_cached_dbm extends getID3
|
|||
function __destruct() {
|
||||
|
||||
// Close dbm file
|
||||
@dba_close($this->dba);
|
||||
dba_close($this->dba);
|
||||
|
||||
// Release exclusive lock
|
||||
@flock($this->lock, LOCK_UN);
|
||||
flock($this->lock, LOCK_UN);
|
||||
|
||||
// Close lock file
|
||||
@fclose($this->lock);
|
||||
fclose($this->lock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -176,13 +165,13 @@ class getID3_cached_dbm extends getID3
|
|||
$this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
|
||||
|
||||
if (!$this->dba) {
|
||||
die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename);
|
||||
throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
|
||||
}
|
||||
|
||||
// Insert getID3 version number
|
||||
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
|
||||
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
||||
|
||||
// Reregister shutdown function
|
||||
// Re-register shutdown function
|
||||
register_shutdown_function(array($this, '__destruct'));
|
||||
}
|
||||
|
||||
|
@ -194,7 +183,7 @@ class getID3_cached_dbm extends getID3
|
|||
if (file_exists($filename)) {
|
||||
|
||||
// Calc key filename::mod_time::size - should be unique
|
||||
$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename);
|
||||
$key = $filename.'::'.filemtime($filename).'::'.filesize($filename);
|
||||
|
||||
// Loopup key
|
||||
$result = dba_fetch($key, $this->dba);
|
|
@ -11,6 +11,7 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// This extension written by Allan Hansen <ahØartemis*dk> //
|
||||
// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -33,8 +34,8 @@
|
|||
*
|
||||
* require_once 'getid3/getid3.php';
|
||||
* require_once 'getid3/getid3/extension.cache.mysql.php';
|
||||
* $getID3 = new getID3_cached_mysql('localhost', 'database',
|
||||
* 'username', 'password');
|
||||
* // 5th parameter (tablename) is optional, default is 'getid3_cache'
|
||||
* $getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password', 'tablename');
|
||||
* $getID3->encoding = 'UTF-8';
|
||||
* $info1 = $getID3->analyze('file1.flac');
|
||||
* $info2 = $getID3->analyze('file2.wv');
|
||||
|
@ -78,31 +79,36 @@ class getID3_cached_mysql extends getID3
|
|||
|
||||
|
||||
// public: constructor - see top of this file for cache type and cache_options
|
||||
function getID3_cached_mysql($host, $database, $username, $password) {
|
||||
function getID3_cached_mysql($host, $database, $username, $password, $table='getid3_cache') {
|
||||
|
||||
// Check for mysql support
|
||||
if (!function_exists('mysql_pconnect')) {
|
||||
die('PHP not compiled with mysql support.');
|
||||
throw new Exception('PHP not compiled with mysql support.');
|
||||
}
|
||||
|
||||
// Connect to database
|
||||
$this->connection = mysql_pconnect($host, $username, $password);
|
||||
if (!$this->connection) {
|
||||
die('mysql_pconnect() failed - check permissions and spelling.');
|
||||
throw new Exception('mysql_pconnect() failed - check permissions and spelling.');
|
||||
}
|
||||
|
||||
// Select database
|
||||
if (!mysql_select_db($database, $this->connection)) {
|
||||
die('Cannot use database '.$database);
|
||||
throw new Exception('Cannot use database '.$database);
|
||||
}
|
||||
|
||||
// Set table
|
||||
$this->table = $table;
|
||||
|
||||
// Create cache table if not exists
|
||||
$this->create_table();
|
||||
|
||||
// Check version number and clear cache if changed
|
||||
$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection);
|
||||
list($version) = @mysql_fetch_array($this->cursor);
|
||||
if ($version != GETID3_VERSION) {
|
||||
$version = '';
|
||||
if ($this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string(getID3::VERSION)."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection)) {
|
||||
list($version) = mysql_fetch_array($this->cursor);
|
||||
}
|
||||
if ($version != getID3::VERSION) {
|
||||
$this->clear_cache();
|
||||
}
|
||||
|
||||
|
@ -114,8 +120,8 @@ class getID3_cached_mysql extends getID3
|
|||
// public: clear cache
|
||||
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("DELETE FROM `".mysql_real_escape_string($this->table)."`", $this->connection);
|
||||
$this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` VALUES ('".getID3::VERSION."', -1, -1, -1, '".getID3::VERSION."')", $this->connection);
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,35 +134,32 @@ class getID3_cached_mysql extends getID3
|
|||
// Short-hands
|
||||
$filetime = filemtime($filename);
|
||||
$filesize = filesize($filename);
|
||||
$filenam2 = mysql_escape_string($filename);
|
||||
|
||||
// Loopup file
|
||||
$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection);
|
||||
list($result) = @mysql_fetch_array($this->cursor);
|
||||
|
||||
// Hit
|
||||
if ($result) {
|
||||
return unserialize($result);
|
||||
// Lookup file
|
||||
$this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string($filename)."') AND (`filesize` = '".mysql_real_escape_string($filesize)."') AND (`filetime` = '".mysql_real_escape_string($filetime)."')", $this->connection);
|
||||
if (mysql_num_rows($this->cursor) > 0) {
|
||||
// Hit
|
||||
list($result) = mysql_fetch_array($this->cursor);
|
||||
return unserialize(base64_decode($result));
|
||||
}
|
||||
}
|
||||
|
||||
// Miss
|
||||
$result = parent::analyze($filename);
|
||||
$analysis = parent::analyze($filename);
|
||||
|
||||
// Save result
|
||||
if (file_exists($filename)) {
|
||||
$res2 = mysql_escape_string(serialize($result));
|
||||
$this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection);
|
||||
$this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".mysql_real_escape_string($filename)."', '".mysql_real_escape_string($filesize)."', '".mysql_real_escape_string($filetime)."', '".mysql_real_escape_string(time())."', '".mysql_real_escape_string(base64_encode(serialize($analysis)))."')", $this->connection);
|
||||
}
|
||||
return $result;
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// private: (re)create sql table
|
||||
function create_table($drop = false) {
|
||||
function create_table($drop=false) {
|
||||
|
||||
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
|
||||
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `".mysql_real_escape_string($this->table)."` (
|
||||
`filename` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`filesize` INT(11) NOT NULL DEFAULT '0',
|
||||
`filetime` INT(11) NOT NULL DEFAULT '0',
|
||||
|
@ -167,5 +170,4 @@ class getID3_cached_mysql extends getID3
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -19,21 +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 |
|
||||
//+---+---+---+---+---+---+---+---+---+---+
|
||||
@fseek($fd, 0);
|
||||
$buffer = @fread($fd, $ThisFileInfo['filesize']);
|
||||
|
||||
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) {
|
||||
|
@ -59,7 +66,7 @@ class getid3_gzip {
|
|||
}
|
||||
}
|
||||
|
||||
$ThisFileInfo['gzip']['files'] = array();
|
||||
$info['gzip']['files'] = array();
|
||||
|
||||
$fpointer = 0;
|
||||
$idx = 0;
|
||||
|
@ -67,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;
|
||||
}
|
||||
|
||||
|
@ -99,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 ...|
|
||||
|
@ -133,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'] = eregi_replace('.gz$', '', $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++;
|
||||
}
|
||||
}
|
||||
|
@ -148,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++;
|
||||
}
|
||||
}
|
||||
|
@ -162,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
|
||||
|
@ -190,44 +197,46 @@ 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
|
||||
switch (@$determined_format['module']) {
|
||||
case 'tar':
|
||||
// file format is determined
|
||||
$determined_format['module'] = (isset($determined_format['module']) ? $determined_format['module'] : '');
|
||||
switch ($determined_format['module']) {
|
||||
case 'tar':
|
||||
// view TAR-file info
|
||||
if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && @include_once(GETID3_INCLUDEPATH.$determined_format['include'])) {
|
||||
if (($temp_tar_filename = tempnam('*', 'getID3')) === false) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// unknown or unhandled format
|
||||
break;
|
||||
case '':
|
||||
default:
|
||||
// unknown or unhandled format
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +263,7 @@ class getid3_gzip {
|
|||
'13' => 'Acorn RISCOS',
|
||||
'255' => 'unknown'
|
||||
);
|
||||
return @$os_type[$key];
|
||||
return (isset($os_type[$key]) ? $os_type[$key] : '');
|
||||
}
|
||||
|
||||
// Converts the eXtra FLags
|
||||
|
@ -264,7 +273,7 @@ class getid3_gzip {
|
|||
'2' => 'maximum compression',
|
||||
'4' => 'fastest algorithm'
|
||||
);
|
||||
return @$xflag_type[$key];
|
||||
return (isset($xflag_type[$key]) ? $xflag_type[$key] : '');
|
||||
}
|
||||
}
|
||||
|
|
@ -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['filename'])) {
|
||||
$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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -19,18 +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
|
||||
|
||||
@fseek($fd, 0);
|
||||
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;
|
||||
}
|
||||
|
@ -47,22 +50,22 @@ class getid3_tar {
|
|||
$checksum += ord($buffer{$i});
|
||||
}
|
||||
$attr = unpack($unpack_header, $buffer);
|
||||
$name = trim(@$attr['fname']);
|
||||
$mode = octdec(trim(@$attr['mode']));
|
||||
$uid = octdec(trim(@$attr['uid']));
|
||||
$gid = octdec(trim(@$attr['gid']));
|
||||
$size = octdec(trim(@$attr['size']));
|
||||
$mtime = octdec(trim(@$attr['mtime']));
|
||||
$chksum = octdec(trim(@$attr['chksum']));
|
||||
$typflag = trim(@$attr['typflag']);
|
||||
$lnkname = trim(@$attr['lnkname']);
|
||||
$magic = trim(@$attr['magic']);
|
||||
$ver = trim(@$attr['ver']);
|
||||
$uname = trim(@$attr['uname']);
|
||||
$gname = trim(@$attr['gname']);
|
||||
$devmaj = octdec(trim(@$attr['devmaj']));
|
||||
$devmin = octdec(trim(@$attr['devmin']));
|
||||
$prefix = trim(@$attr['prefix']);
|
||||
$name = (isset($attr['fname'] ) ? trim($attr['fname'] ) : '');
|
||||
$mode = octdec(isset($attr['mode'] ) ? trim($attr['mode'] ) : '');
|
||||
$uid = octdec(isset($attr['uid'] ) ? trim($attr['uid'] ) : '');
|
||||
$gid = octdec(isset($attr['gid'] ) ? trim($attr['gid'] ) : '');
|
||||
$size = octdec(isset($attr['size'] ) ? trim($attr['size'] ) : '');
|
||||
$mtime = octdec(isset($attr['mtime'] ) ? trim($attr['mtime'] ) : '');
|
||||
$chksum = octdec(isset($attr['chksum'] ) ? trim($attr['chksum'] ) : '');
|
||||
$typflag = (isset($attr['typflag']) ? trim($attr['typflag']) : '');
|
||||
$lnkname = (isset($attr['lnkname']) ? trim($attr['lnkname']) : '');
|
||||
$magic = (isset($attr['magic'] ) ? trim($attr['magic'] ) : '');
|
||||
$ver = (isset($attr['ver'] ) ? trim($attr['ver'] ) : '');
|
||||
$uname = (isset($attr['uname'] ) ? trim($attr['uname'] ) : '');
|
||||
$gname = (isset($attr['gname'] ) ? trim($attr['gname'] ) : '');
|
||||
$devmaj = octdec(isset($attr['devmaj'] ) ? trim($attr['devmaj'] ) : '');
|
||||
$devmin = octdec(isset($attr['devmin'] ) ? trim($attr['devmin'] ) : '');
|
||||
$prefix = (isset($attr['prefix'] ) ? trim($attr['prefix'] ) : '');
|
||||
if (($checksum == 256) && ($chksum == 0)) {
|
||||
// EOF Found
|
||||
break;
|
||||
|
@ -79,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),
|
||||
|
@ -108,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;
|
||||
}
|
||||
|
@ -167,7 +170,7 @@ class getid3_tar {
|
|||
'S' => 'LF_SPARSE',
|
||||
'V' => 'LF_VOLHDR'
|
||||
);
|
||||
return @$flag_types[$typflag];
|
||||
return (isset($flag_types[$typflag]) ? $flag_types[$typflag] : '');
|
||||
}
|
||||
|
||||
}
|
|
@ -14,63 +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 ($ThisFileInfo['filesize'] < pow(2, 31)) {
|
||||
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;
|
||||
|
@ -79,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;
|
||||
}
|
||||
|
||||
|
@ -168,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));
|
||||
|
@ -200,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']);
|
||||
|
@ -210,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));
|
||||
|
@ -225,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));
|
||||
|
@ -265,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']);
|
||||
|
@ -281,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));
|
||||
|
@ -301,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) {
|
||||
|
@ -341,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',
|
||||
|
@ -366,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',
|
||||
|
@ -384,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
|
@ -0,0 +1,73 @@
|
|||
<?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.audio.bink.php //
|
||||
// module for analyzing Bink or Smacker audio-video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_bink extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$info['error'][] = 'Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']';
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$fileTypeID = fread($this->getid3->fp, 3);
|
||||
switch ($fileTypeID) {
|
||||
case 'BIK':
|
||||
return $this->ParseBink();
|
||||
break;
|
||||
|
||||
case 'SMK':
|
||||
return $this->ParseSmacker();
|
||||
break;
|
||||
|
||||
default:
|
||||
$info['error'][] = 'Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function ParseBink() {
|
||||
$info = &$this->getid3->info;
|
||||
$info['fileformat'] = 'bink';
|
||||
$info['video']['dataformat'] = 'bink';
|
||||
|
||||
$fileData = 'BIK'.fread($this->getid3->fp, 13);
|
||||
|
||||
$info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
||||
$info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
|
||||
|
||||
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() {
|
||||
$info = &$this->getid3->info;
|
||||
$info['fileformat'] = 'smacker';
|
||||
$info['video']['dataformat'] = 'smacker';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,731 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
// //
|
||||
// FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
|
||||
// //
|
||||
// * version 0.1 (26 June 2005) //
|
||||
// //
|
||||
// //
|
||||
// * version 0.1.1 (15 July 2005) //
|
||||
// minor modifications by James Heinrich <info@getid3.org> //
|
||||
// //
|
||||
// * version 0.2 (22 February 2006) //
|
||||
// Support for On2 VP6 codec and meta information //
|
||||
// by Steve Webster <steve.webster@featurecreep.com> //
|
||||
// //
|
||||
// * version 0.3 (15 June 2006) //
|
||||
// Modified to not read entire file into memory //
|
||||
// by James Heinrich <info@getid3.org> //
|
||||
// //
|
||||
// * 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.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.6 (24 May 2009) //
|
||||
// Better parsing of files with h264 video //
|
||||
// by Evgeny Moysevich <moysevichØgmail*com> //
|
||||
// //
|
||||
// * version 0.6.1 (30 May 2011) //
|
||||
// prevent infinite loops in expGolombUe() //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.flv.php //
|
||||
// module for analyzing Shockwave Flash Video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
define('GETID3_FLV_TAG_AUDIO', 8);
|
||||
define('GETID3_FLV_TAG_VIDEO', 9);
|
||||
define('GETID3_FLV_TAG_META', 18);
|
||||
|
||||
define('GETID3_FLV_VIDEO_H263', 2);
|
||||
define('GETID3_FLV_VIDEO_SCREEN', 3);
|
||||
define('GETID3_FLV_VIDEO_VP6FLV', 4);
|
||||
define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
|
||||
define('GETID3_FLV_VIDEO_SCREENV2', 6);
|
||||
define('GETID3_FLV_VIDEO_H264', 7);
|
||||
|
||||
define('H264_AVC_SEQUENCE_HEADER', 0);
|
||||
define('H264_PROFILE_BASELINE', 66);
|
||||
define('H264_PROFILE_MAIN', 77);
|
||||
define('H264_PROFILE_EXTENDED', 88);
|
||||
define('H264_PROFILE_HIGH', 100);
|
||||
define('H264_PROFILE_HIGH10', 110);
|
||||
define('H264_PROFILE_HIGH422', 122);
|
||||
define('H264_PROFILE_HIGH444', 144);
|
||||
define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
|
||||
|
||||
class getid3_flv extends getid3_handler
|
||||
{
|
||||
var $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
|
||||
$FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
|
||||
$FLVheader = fread($this->getid3->fp, 5);
|
||||
|
||||
$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));
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
|
||||
$info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
|
||||
|
||||
$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4));
|
||||
$FLVheaderFrameLength = 9;
|
||||
if ($FrameSizeDataLength > $FLVheaderFrameLength) {
|
||||
fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
|
||||
}
|
||||
$Duration = 0;
|
||||
$found_video = false;
|
||||
$found_audio = false;
|
||||
$found_meta = false;
|
||||
$found_valid_meta_playtime = false;
|
||||
$tagParseCount = 0;
|
||||
$info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
|
||||
$flv_framecount = &$info['flv']['framecount'];
|
||||
while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
|
||||
$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($this->getid3->fp) - 1 + $DataLength;
|
||||
if ($Timestamp > $Duration) {
|
||||
$Duration = $Timestamp;
|
||||
}
|
||||
|
||||
$flv_framecount['total']++;
|
||||
switch ($TagType) {
|
||||
case GETID3_FLV_TAG_AUDIO:
|
||||
$flv_framecount['audio']++;
|
||||
if (!$found_audio) {
|
||||
$found_audio = true;
|
||||
$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:
|
||||
$flv_framecount['video']++;
|
||||
if (!$found_video) {
|
||||
$found_video = true;
|
||||
$info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
|
||||
|
||||
$FLVvideoHeader = fread($this->getid3->fp, 11);
|
||||
|
||||
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));
|
||||
if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
|
||||
// read AVCDecoderConfigurationRecord
|
||||
$configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
|
||||
$AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
|
||||
$profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
|
||||
$lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
|
||||
$numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
|
||||
|
||||
if (($numOfSequenceParameterSets & 0x1F) != 0) {
|
||||
// there is at least one SequenceParameterSet
|
||||
// read size of the first SequenceParameterSet
|
||||
//$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
|
||||
$spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
|
||||
// read the first SequenceParameterSet
|
||||
$sps = fread($this->getid3->fp, $spsSize);
|
||||
if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
|
||||
$spsReader = new AVCSequenceParameterSetReader($sps);
|
||||
$spsReader->readData();
|
||||
$info['video']['resolution_x'] = $spsReader->getWidth();
|
||||
$info['video']['resolution_y'] = $spsReader->getHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
// end: moysevichØgmail*com
|
||||
|
||||
} elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
|
||||
|
||||
$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
|
||||
$PictureSizeType = $PictureSizeType & 0x0007;
|
||||
$info['flv']['header']['videoSizeType'] = $PictureSizeType;
|
||||
switch ($PictureSizeType) {
|
||||
case 0:
|
||||
//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
|
||||
//$PictureSizeEnc <<= 1;
|
||||
//$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
|
||||
//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
|
||||
//$PictureSizeEnc <<= 1;
|
||||
//$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;
|
||||
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
|
||||
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
|
||||
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
|
||||
$PictureSizeEnc['x'] >>= 7;
|
||||
$PictureSizeEnc['y'] >>= 7;
|
||||
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
|
||||
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$info['video']['resolution_x'] = 352;
|
||||
$info['video']['resolution_y'] = 288;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$info['video']['resolution_x'] = 176;
|
||||
$info['video']['resolution_y'] = 144;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
$info['video']['resolution_x'] = 128;
|
||||
$info['video']['resolution_y'] = 96;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$info['video']['resolution_x'] = 320;
|
||||
$info['video']['resolution_y'] = 240;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$info['video']['resolution_x'] = 160;
|
||||
$info['video']['resolution_y'] = 120;
|
||||
break;
|
||||
|
||||
default:
|
||||
$info['video']['resolution_x'] = 0;
|
||||
$info['video']['resolution_y'] = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
$info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
|
||||
}
|
||||
break;
|
||||
|
||||
// Meta tag
|
||||
case GETID3_FLV_TAG_META:
|
||||
if (!$found_meta) {
|
||||
$found_meta = true;
|
||||
fseek($this->getid3->fp, -1, SEEK_CUR);
|
||||
$datachunk = fread($this->getid3->fp, $DataLength);
|
||||
$AMFstream = new AMFStream($datachunk);
|
||||
$reader = new AMFReader($AMFstream);
|
||||
$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($info['flv']['meta']['onMetaData'][$sourcekey])) {
|
||||
switch ($sourcekey) {
|
||||
case 'width':
|
||||
case 'height':
|
||||
$info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
|
||||
break;
|
||||
case 'audiodatarate':
|
||||
$info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
|
||||
break;
|
||||
case 'videodatarate':
|
||||
case 'frame_rate':
|
||||
default:
|
||||
$info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
|
||||
$found_valid_meta_playtime = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
fseek($this->getid3->fp, $NextOffset, SEEK_SET);
|
||||
}
|
||||
|
||||
$info['playtime_seconds'] = $Duration / 1000;
|
||||
if ($info['playtime_seconds'] > 0) {
|
||||
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||
}
|
||||
|
||||
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']);
|
||||
|
||||
$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($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 (!empty($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($info['flv']['meta']['onMetaData']['audiocodecid'])) {
|
||||
$info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
|
||||
}
|
||||
if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
|
||||
$info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLVaudioFormat($id) {
|
||||
$FLVaudioFormat = array(
|
||||
0 => 'Linear PCM, platform endian',
|
||||
1 => 'ADPCM',
|
||||
2 => 'mp3',
|
||||
3 => 'Linear PCM, little endian',
|
||||
4 => 'Nellymoser 16kHz mono',
|
||||
5 => 'Nellymoser 8kHz mono',
|
||||
6 => 'Nellymoser',
|
||||
7 => 'G.711A-law logarithmic PCM',
|
||||
8 => 'G.711 mu-law logarithmic PCM',
|
||||
9 => 'reserved',
|
||||
10 => 'AAC',
|
||||
11 => false, // unknown?
|
||||
12 => false, // unknown?
|
||||
13 => false, // unknown?
|
||||
14 => 'mp3 8kHz',
|
||||
15 => 'Device-specific sound',
|
||||
);
|
||||
return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioRate($id) {
|
||||
$FLVaudioRate = array(
|
||||
0 => 5500,
|
||||
1 => 11025,
|
||||
2 => 22050,
|
||||
3 => 44100,
|
||||
);
|
||||
return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioBitDepth($id) {
|
||||
$FLVaudioBitDepth = array(
|
||||
0 => 8,
|
||||
1 => 16,
|
||||
);
|
||||
return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false);
|
||||
}
|
||||
|
||||
function FLVvideoCodec($id) {
|
||||
$FLVvideoCodec = array(
|
||||
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
|
||||
GETID3_FLV_VIDEO_SCREEN => 'Screen video',
|
||||
GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
|
||||
GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
|
||||
GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
|
||||
GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
|
||||
);
|
||||
return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false);
|
||||
}
|
||||
}
|
||||
|
||||
class AMFStream {
|
||||
var $bytes;
|
||||
var $pos;
|
||||
|
||||
function AMFStream(&$bytes) {
|
||||
$this->bytes =& $bytes;
|
||||
$this->pos = 0;
|
||||
}
|
||||
|
||||
function readByte() {
|
||||
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
|
||||
}
|
||||
|
||||
function readInt() {
|
||||
return ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readLong() {
|
||||
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return getid3_lib::BigEndian2Float($this->read(8));
|
||||
}
|
||||
|
||||
function readUTF() {
|
||||
$length = $this->readInt();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function readLongUTF() {
|
||||
$length = $this->readLong();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function read($length) {
|
||||
$val = substr($this->bytes, $this->pos, $length);
|
||||
$this->pos += $length;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekByte() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readByte();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekInt() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readInt();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLong() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLong();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekDouble() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readDouble();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLongUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLongUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
class AMFReader {
|
||||
var $stream;
|
||||
|
||||
function AMFReader(&$stream) {
|
||||
$this->stream =& $stream;
|
||||
}
|
||||
|
||||
function readData() {
|
||||
$value = null;
|
||||
|
||||
$type = $this->stream->readByte();
|
||||
switch ($type) {
|
||||
|
||||
// Double
|
||||
case 0:
|
||||
$value = $this->readDouble();
|
||||
break;
|
||||
|
||||
// Boolean
|
||||
case 1:
|
||||
$value = $this->readBoolean();
|
||||
break;
|
||||
|
||||
// String
|
||||
case 2:
|
||||
$value = $this->readString();
|
||||
break;
|
||||
|
||||
// Object
|
||||
case 3:
|
||||
$value = $this->readObject();
|
||||
break;
|
||||
|
||||
// null
|
||||
case 6:
|
||||
return null;
|
||||
break;
|
||||
|
||||
// Mixed array
|
||||
case 8:
|
||||
$value = $this->readMixedArray();
|
||||
break;
|
||||
|
||||
// Array
|
||||
case 10:
|
||||
$value = $this->readArray();
|
||||
break;
|
||||
|
||||
// Date
|
||||
case 11:
|
||||
$value = $this->readDate();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
case 13:
|
||||
$value = $this->readLongString();
|
||||
break;
|
||||
|
||||
// XML (handled as string)
|
||||
case 15:
|
||||
$value = $this->readXML();
|
||||
break;
|
||||
|
||||
// Typed object (handled as object)
|
||||
case 16:
|
||||
$value = $this->readTypedObject();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
default:
|
||||
$value = '(unknown or unsupported data type)';
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return $this->stream->readDouble();
|
||||
}
|
||||
|
||||
function readBoolean() {
|
||||
return $this->stream->readByte() == 1;
|
||||
}
|
||||
|
||||
function readString() {
|
||||
return $this->stream->readUTF();
|
||||
}
|
||||
|
||||
function readObject() {
|
||||
// Get highest numerical index - ignored
|
||||
// $highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readMixedArray() {
|
||||
// Get highest numerical index - ignored
|
||||
$highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
if (is_numeric($key)) {
|
||||
$key = (float) $key;
|
||||
}
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readArray() {
|
||||
$length = $this->stream->readLong();
|
||||
$data = array();
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$data[] = $this->readData();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readDate() {
|
||||
$timestamp = $this->stream->readDouble();
|
||||
$timezone = $this->stream->readInt();
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
function readLongString() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readXML() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readTypedObject() {
|
||||
$className = $this->stream->readUTF();
|
||||
return $this->readObject();
|
||||
}
|
||||
}
|
||||
|
||||
class AVCSequenceParameterSetReader {
|
||||
var $sps;
|
||||
var $start = 0;
|
||||
var $currentBytes = 0;
|
||||
var $currentBits = 0;
|
||||
var $width;
|
||||
var $height;
|
||||
|
||||
function AVCSequenceParameterSetReader($sps) {
|
||||
$this->sps = $sps;
|
||||
}
|
||||
|
||||
function readData() {
|
||||
$this->skipBits(8);
|
||||
$this->skipBits(8);
|
||||
$profile = $this->getBits(8); // read profile
|
||||
$this->skipBits(16);
|
||||
$this->expGolombUe(); // read sps id
|
||||
if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) {
|
||||
if ($this->expGolombUe() == 3) {
|
||||
$this->skipBits(1);
|
||||
}
|
||||
$this->expGolombUe();
|
||||
$this->expGolombUe();
|
||||
$this->skipBits(1);
|
||||
if ($this->getBit()) {
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
if ($this->getBit()) {
|
||||
$size = $i < 6 ? 16 : 64;
|
||||
$lastScale = 8;
|
||||
$nextScale = 8;
|
||||
for ($j = 0; $j < $size; $j++) {
|
||||
if ($nextScale != 0) {
|
||||
$deltaScale = $this->expGolombUe();
|
||||
$nextScale = ($lastScale + $deltaScale + 256) % 256;
|
||||
}
|
||||
if ($nextScale != 0) {
|
||||
$lastScale = $nextScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->expGolombUe();
|
||||
$pocType = $this->expGolombUe();
|
||||
if ($pocType == 0) {
|
||||
$this->expGolombUe();
|
||||
} elseif ($pocType == 1) {
|
||||
$this->skipBits(1);
|
||||
$this->expGolombSe();
|
||||
$this->expGolombSe();
|
||||
$pocCycleLength = $this->expGolombUe();
|
||||
for ($i = 0; $i < $pocCycleLength; $i++) {
|
||||
$this->expGolombSe();
|
||||
}
|
||||
}
|
||||
$this->expGolombUe();
|
||||
$this->skipBits(1);
|
||||
$this->width = ($this->expGolombUe() + 1) * 16;
|
||||
$heightMap = $this->expGolombUe() + 1;
|
||||
$this->height = (2 - $this->getBit()) * $heightMap * 16;
|
||||
}
|
||||
|
||||
function skipBits($bits) {
|
||||
$newBits = $this->currentBits + $bits;
|
||||
$this->currentBytes += (int)floor($newBits / 8);
|
||||
$this->currentBits = $newBits % 8;
|
||||
}
|
||||
|
||||
function getBit() {
|
||||
$result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
|
||||
$this->skipBits(1);
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getBits($bits) {
|
||||
$result = 0;
|
||||
for ($i = 0; $i < $bits; $i++) {
|
||||
$result = ($result << 1) + $this->getBit();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function expGolombUe() {
|
||||
$significantBits = 0;
|
||||
$bit = $this->getBit();
|
||||
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;
|
||||
}
|
||||
|
||||
function expGolombSe() {
|
||||
$result = $this->expGolombUe();
|
||||
if (($result & 0x01) == 0) {
|
||||
return -($result >> 1);
|
||||
} else {
|
||||
return ($result + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
function getWidth() {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
function getHeight() {
|
||||
return $this->height;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
<?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.audio.nsv.php //
|
||||
// module for analyzing Nullsoft NSV files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_nsv extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$NSVheader = fread($this->getid3->fp, 4);
|
||||
|
||||
switch ($NSVheader) {
|
||||
case 'NSVs':
|
||||
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(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:
|
||||
$info['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($info['nsv']['NSVf'])) {
|
||||
$info['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNSVsHeaderFilepointer($fileoffset) {
|
||||
$info = &$this->getid3->info;
|
||||
fseek($this->getid3->fp, $fileoffset, SEEK_SET);
|
||||
$NSVsheader = fread($this->getid3->fp, 28);
|
||||
$offset = 0;
|
||||
|
||||
$info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
||||
|
||||
$info['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$info['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
//$info['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
switch ($info['nsv']['NSVs']['audio_codec']) {
|
||||
case 'PCM ':
|
||||
$info['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$info['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$info['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$info['audio']['sample_rate'] = $info['nsv']['NSVs']['sample_rate'];
|
||||
break;
|
||||
|
||||
case 'MP3 ':
|
||||
case 'NONE':
|
||||
default:
|
||||
//$info['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
|
||||
$offset += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
$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($fileoffset, $getTOCoffsets=false) {
|
||||
$info = &$this->getid3->info;
|
||||
fseek($this->getid3->fp, $fileoffset, SEEK_SET);
|
||||
$NSVfheader = fread($this->getid3->fp, 28);
|
||||
$offset = 0;
|
||||
|
||||
$info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
|
||||
$offset += 4;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
||||
|
||||
$info['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
$info['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
|
||||
$info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$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);
|
||||
$info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
|
||||
$offset += $info['nsv']['NSVf']['meta_size'];
|
||||
|
||||
if ($getTOCoffsets) {
|
||||
$TOCcounter = 0;
|
||||
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($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);
|
||||
$info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
|
||||
$info['bitrate'] = ($info['nsv']['NSVf']['file_size'] * 8) / $info['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static function NSVframerateLookup($framerateindex) {
|
||||
if ($framerateindex <= 127) {
|
||||
return (float) $framerateindex;
|
||||
}
|
||||
static $NSVframerateLookup = array();
|
||||
if (empty($NSVframerateLookup)) {
|
||||
$NSVframerateLookup[129] = (float) 29.970;
|
||||
$NSVframerateLookup[131] = (float) 23.976;
|
||||
$NSVframerateLookup[133] = (float) 14.985;
|
||||
$NSVframerateLookup[197] = (float) 59.940;
|
||||
$NSVframerateLookup[199] = (float) 47.952;
|
||||
}
|
||||
return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -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 http://www.getid3.org/upload/ or 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
|
@ -14,59 +14,52 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
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));
|
||||
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
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);
|
||||
if ($decompressed = @gzuncompress($SWFfileData)) {
|
||||
|
||||
$SWFfileData = $SWFHead.$decompressed;
|
||||
|
||||
} else {
|
||||
|
||||
$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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||
|
||||
$FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
|
||||
$FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
|
||||
|
@ -75,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.
|
||||
|
@ -85,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>';
|
||||
|
||||
|
@ -126,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;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?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.audio.aa.php //
|
||||
// module for analyzing Audible Audiobook files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_aa extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
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) {
|
||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AAheader, 4, 4)).'"';
|
||||
return false;
|
||||
}
|
||||
|
||||
// shortcut
|
||||
$info['aa'] = array();
|
||||
$thisfile_au = &$info['aa'];
|
||||
|
||||
$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'] > ($info['avdataend'] - $info['avdataoffset'])) {
|
||||
$info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"';
|
||||
}
|
||||
|
||||
$info['audio']['bits_per_sample'] = 16; // is it?
|
||||
$info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
|
||||
$info['audio']['channels'] = $thisfile_au['channels'];
|
||||
|
||||
//$info['playtime_seconds'] = 0;
|
||||
//$info['audio']['bitrate'] = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,515 @@
|
|||
<?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.audio.aac.php //
|
||||
// module for analyzing AAC Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_aac extends getid3_handler
|
||||
{
|
||||
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() {
|
||||
$info = &$this->getid3->info;
|
||||
$info['fileformat'] = 'aac';
|
||||
$info['audio']['dataformat'] = 'aac';
|
||||
$info['audio']['lossless'] = false;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$AACheader = fread($this->getid3->fp, 1024);
|
||||
$offset = 0;
|
||||
|
||||
if (substr($AACheader, 0, 4) == 'ADIF') {
|
||||
|
||||
// http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
||||
|
||||
// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
||||
// adif_header() {
|
||||
// adif_id 32
|
||||
// copyright_id_present 1
|
||||
// if( copyright_id_present )
|
||||
// copyright_id 72
|
||||
// original_copy 1
|
||||
// home 1
|
||||
// bitstream_type 1
|
||||
// bitrate 23
|
||||
// num_program_config_elements 4
|
||||
// for (i = 0; i < num_program_config_elements + 1; i++ ) {
|
||||
// if( bitstream_type == '0' )
|
||||
// adif_buffer_fullness 20
|
||||
// program_config_element()
|
||||
// }
|
||||
// }
|
||||
|
||||
$AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
|
||||
$bitoffset = 0;
|
||||
|
||||
$info['aac']['header_type'] = 'ADIF';
|
||||
$bitoffset += 32;
|
||||
$info['aac']['header']['mpeg_version'] = 4;
|
||||
|
||||
$info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
if ($info['aac']['header']['copyright']) {
|
||||
$info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
|
||||
$bitoffset += 72;
|
||||
}
|
||||
$info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
$info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
$info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||
$bitoffset += 1;
|
||||
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 {
|
||||
$info['audio']['bitrate_mode'] = 'cbr';
|
||||
$info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
||||
$bitoffset += 23;
|
||||
$info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
|
||||
}
|
||||
if ($info['audio']['bitrate'] == 0) {
|
||||
$info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
|
||||
return false;
|
||||
}
|
||||
$info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
|
||||
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
|
||||
|
||||
// element_instance_tag 4
|
||||
// object_type 2
|
||||
// sampling_frequency_index 4
|
||||
// num_front_channel_elements 4
|
||||
// num_side_channel_elements 4
|
||||
// num_back_channel_elements 4
|
||||
// num_lfe_channel_elements 2
|
||||
// num_assoc_data_elements 3
|
||||
// num_valid_cc_elements 4
|
||||
// mono_mixdown_present 1
|
||||
// mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
||||
// stereo_mixdown_present 1
|
||||
// stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
||||
// matrix_mixdown_idx_present 1
|
||||
// matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
||||
// pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
||||
// for (i = 0; i < num_front_channel_elements; i++) {
|
||||
// front_element_is_cpe[i] 1
|
||||
// front_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_side_channel_elements; i++) {
|
||||
// side_element_is_cpe[i] 1
|
||||
// side_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_back_channel_elements; i++) {
|
||||
// back_element_is_cpe[i] 1
|
||||
// back_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_lfe_channel_elements; i++) {
|
||||
// lfe_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_assoc_data_elements; i++) {
|
||||
// assoc_data_element_tag_select[i] 4
|
||||
// }
|
||||
// for (i = 0; i < num_valid_cc_elements; i++) {
|
||||
// cc_element_is_ind_sw[i] 1
|
||||
// valid_cc_element_tag_select[i] 4
|
||||
// }
|
||||
// byte_alignment() VAR
|
||||
// comment_field_bytes 8
|
||||
// for (i = 0; i < comment_field_bytes; i++) {
|
||||
// comment_field_data[i] 8
|
||||
// }
|
||||
|
||||
if (!$info['aac']['header']['is_vbr']) {
|
||||
$info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
|
||||
$bitoffset += 20;
|
||||
}
|
||||
$info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||
$bitoffset += 2;
|
||||
$info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
||||
$bitoffset += 3;
|
||||
$info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
$info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
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;
|
||||
}
|
||||
$info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
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;
|
||||
}
|
||||
$info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$bitoffset += 1;
|
||||
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;
|
||||
$info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||
$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;
|
||||
$info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
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;
|
||||
$info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$bitoffset += 4;
|
||||
}
|
||||
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;
|
||||
$info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||
$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 < $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 < $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;
|
||||
$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;
|
||||
|
||||
$info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
|
||||
$bitoffset += 8;
|
||||
$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'];
|
||||
|
||||
|
||||
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
|
||||
$info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
|
||||
$info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
|
||||
$info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
|
||||
if ($info['aac']['program_configs'][$i]['comment_field']) {
|
||||
$info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
|
||||
}
|
||||
}
|
||||
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
||||
|
||||
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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 // 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
|
||||
// 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 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
|
||||
// copyright_identification_start 1
|
||||
// aac_frame_length 13 length of the frame including header (in bytes)
|
||||
// adts_buffer_fullness 11 0x7FF indicates VBR
|
||||
// no_raw_data_blocks_in_frame 2
|
||||
|
||||
// * ADTS Error check
|
||||
// crc_check 16 only if protection_absent == 0
|
||||
|
||||
$byteoffset = $info['avdataoffset'];
|
||||
$framenumber = 0;
|
||||
|
||||
// Init bit pattern array
|
||||
static $decbin = array();
|
||||
|
||||
// Populate $bindec
|
||||
for ($i = 0; $i < 256; $i++) {
|
||||
$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// used to calculate bitrate below
|
||||
$BitrateCache = array();
|
||||
|
||||
|
||||
while (true) {
|
||||
// breaks out when end-of-file encountered, or invalid data found,
|
||||
// or MaxFramesToScan frames have been scanned
|
||||
|
||||
if (!getid3_lib::intValueSupported($byteoffset)) {
|
||||
$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($this->getid3->fp, $byteoffset, SEEK_SET);
|
||||
|
||||
// First get substring
|
||||
$substring = fread($this->getid3->fp, 9); // header is 7 bytes (or 9 if CRC is present)
|
||||
$substringlength = strlen($substring);
|
||||
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));
|
||||
|
||||
$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;
|
||||
//}
|
||||
unset($info['aac']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gather info for first frame only - this takes time to do 1000 times!
|
||||
if ($framenumber == 0) {
|
||||
$info['aac']['header_type'] = 'ADTS';
|
||||
$info['fileformat'] = 'aac';
|
||||
$info['audio']['dataformat'] = 'aac';
|
||||
|
||||
$info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
|
||||
$info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
|
||||
$info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
|
||||
|
||||
$info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
|
||||
$info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
|
||||
$info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
|
||||
$info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
|
||||
$info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
|
||||
$info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
|
||||
$info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
|
||||
$info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
|
||||
$info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
|
||||
|
||||
$info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
|
||||
$info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
|
||||
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
|
||||
$info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
|
||||
$info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
|
||||
$info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
|
||||
$info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
|
||||
$info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
|
||||
if ($ReturnExtendedInfo) {
|
||||
$info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
|
||||
$info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
|
||||
}
|
||||
|
||||
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 = ($header2 & 0x0003FFE0) >> 5;
|
||||
|
||||
if (!isset($BitrateCache[$FrameLength])) {
|
||||
$BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
|
||||
}
|
||||
getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
|
||||
|
||||
$info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
|
||||
|
||||
$info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
|
||||
if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
} else {
|
||||
$info['audio']['bitrate_mode'] = 'cbr';
|
||||
}
|
||||
$info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
|
||||
|
||||
if ($info['aac']['header']['crc_present']) {
|
||||
//$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
|
||||
}
|
||||
|
||||
if (!$ReturnExtendedInfo) {
|
||||
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) < $info['avdataend'])) {
|
||||
|
||||
// keep scanning
|
||||
|
||||
} else {
|
||||
|
||||
$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;
|
||||
}
|
||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||
ksort($info['aac']['bitrate_distribution']);
|
||||
|
||||
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
// should never get here.
|
||||
}
|
||||
|
||||
public static function AACsampleRateLookup($samplerateid) {
|
||||
static $AACsampleRateLookup = array();
|
||||
if (empty($AACsampleRateLookup)) {
|
||||
$AACsampleRateLookup[0] = 96000;
|
||||
$AACsampleRateLookup[1] = 88200;
|
||||
$AACsampleRateLookup[2] = 64000;
|
||||
$AACsampleRateLookup[3] = 48000;
|
||||
$AACsampleRateLookup[4] = 44100;
|
||||
$AACsampleRateLookup[5] = 32000;
|
||||
$AACsampleRateLookup[6] = 24000;
|
||||
$AACsampleRateLookup[7] = 22050;
|
||||
$AACsampleRateLookup[8] = 16000;
|
||||
$AACsampleRateLookup[9] = 12000;
|
||||
$AACsampleRateLookup[10] = 11025;
|
||||
$AACsampleRateLookup[11] = 8000;
|
||||
$AACsampleRateLookup[12] = 0;
|
||||
$AACsampleRateLookup[13] = 0;
|
||||
$AACsampleRateLookup[14] = 0;
|
||||
$AACsampleRateLookup[15] = 0;
|
||||
}
|
||||
return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
|
||||
}
|
||||
|
||||
public static function AACprofileLookup($profileid, $mpegversion) {
|
||||
static $AACprofileLookup = array();
|
||||
if (empty($AACprofileLookup)) {
|
||||
$AACprofileLookup[2][0] = 'Main profile';
|
||||
$AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
|
||||
$AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
|
||||
$AACprofileLookup[2][3] = '(reserved)';
|
||||
$AACprofileLookup[4][0] = 'AAC_MAIN';
|
||||
$AACprofileLookup[4][1] = 'AAC_LC';
|
||||
$AACprofileLookup[4][2] = 'AAC_SSR';
|
||||
$AACprofileLookup[4][3] = 'AAC_LTP';
|
||||
}
|
||||
return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
|
||||
}
|
||||
|
||||
public static function AACchannelCountCalculate($program_configs) {
|
||||
$channels = 0;
|
||||
for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['front_element_is_cpe'][$i]) {
|
||||
// each front element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['side_element_is_cpe'][$i]) {
|
||||
// each side element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
if ($program_configs['back_element_is_cpe'][$i]) {
|
||||
// each back element is channel pair (CPE = Channel Pair Element)
|
||||
$channels++;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
|
||||
$channels++;
|
||||
}
|
||||
return $channels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,473 @@
|
|||
<?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.audio.ac3.php //
|
||||
// module for analyzing AC-3 (aka Dolby Digital) audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_ac3 extends getid3_handler
|
||||
{
|
||||
private $AC3header = '';
|
||||
private $BSIoffset = 0;
|
||||
|
||||
|
||||
public function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
///AH
|
||||
$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
|
||||
|
||||
$info['fileformat'] = 'ac3';
|
||||
|
||||
// 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
|
||||
// new audio samples per channel. A synchronization information (SI) header at the beginning
|
||||
// of each frame contains information needed to acquire and maintain synchronization. A
|
||||
// bit stream information (BSI) header follows SI, and contains parameters describing the coded
|
||||
// audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
|
||||
// end of each frame is an error check field that includes a CRC word for error detection. An
|
||||
// additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
|
||||
//
|
||||
// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$this->AC3header['syncinfo'] = fread($this->getid3->fp, 5);
|
||||
$thisfile_ac3_raw['synchinfo']['synchword'] = substr($this->AC3header['syncinfo'], 0, 2);
|
||||
|
||||
$magic = "\x0B\x77";
|
||||
if ($thisfile_ac3_raw['synchinfo']['synchword'] != $magic) {
|
||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_ac3_raw['synchinfo']['synchword']).'"';
|
||||
unset($info['fileformat'], $info['ac3']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['audio']['dataformat'] = 'ac3';
|
||||
$info['audio']['bitrate_mode'] = 'cbr';
|
||||
$info['audio']['lossless'] = false;
|
||||
|
||||
// syncinfo() {
|
||||
// syncword 16
|
||||
// crc1 16
|
||||
// fscod 2
|
||||
// frmsizecod 6
|
||||
// } /* end of syncinfo */
|
||||
|
||||
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 2, 2));
|
||||
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 4, 1));
|
||||
$thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
|
||||
$thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
|
||||
|
||||
$thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
|
||||
if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
|
||||
$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']);
|
||||
$info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
|
||||
|
||||
$this->AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($this->getid3->fp, 15));
|
||||
$ac3_bsi_offset = 0;
|
||||
|
||||
$thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
|
||||
if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
|
||||
// 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.
|
||||
$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;
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
|
||||
$thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
|
||||
|
||||
$thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
|
||||
$ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
|
||||
foreach($ac3_coding_mode as $key => $value) {
|
||||
$thisfile_ac3[$key] = $value;
|
||||
}
|
||||
switch ($thisfile_ac3_raw_bsi['acmod']) {
|
||||
case 0:
|
||||
case 1:
|
||||
$info['audio']['channelmode'] = 'mono';
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
$info['audio']['channelmode'] = 'stereo';
|
||||
break;
|
||||
default:
|
||||
$info['audio']['channelmode'] = 'surround';
|
||||
break;
|
||||
}
|
||||
$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.
|
||||
$thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
|
||||
$thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
|
||||
// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
|
||||
$thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
|
||||
$thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
|
||||
// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
|
||||
$thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
|
||||
$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
|
||||
$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
|
||||
if ($thisfile_ac3_raw_bsi['lfeon']) {
|
||||
//$info['audio']['channels']++;
|
||||
$info['audio']['channels'] .= '.1';
|
||||
}
|
||||
|
||||
$thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
|
||||
|
||||
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
|
||||
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
|
||||
$thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
|
||||
$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
|
||||
|
||||
$thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['compre_flag']) {
|
||||
$thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
|
||||
$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['langcode_flag']) {
|
||||
$thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['audprodie']) {
|
||||
$thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
|
||||
$thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
|
||||
|
||||
$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
|
||||
$thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
|
||||
}
|
||||
|
||||
if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
|
||||
// If acmod is 0, then two completely independent program channels (dual mono)
|
||||
// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
|
||||
// a number of additional items are present in BSI or audblk to fully describe Ch2.
|
||||
|
||||
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
|
||||
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
|
||||
$thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
|
||||
$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
|
||||
|
||||
$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['compre_flag2']) {
|
||||
$thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
|
||||
$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
|
||||
$thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['audprodie2']) {
|
||||
$thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
|
||||
$thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
|
||||
|
||||
$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
|
||||
$thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
|
||||
|
||||
$thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1);
|
||||
|
||||
$thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
|
||||
$thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
|
||||
$thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
|
||||
}
|
||||
|
||||
$thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
|
||||
if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
|
||||
$thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
|
||||
|
||||
$this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($this->getid3->fp, $thisfile_ac3_raw_bsi['addbsi_length']));
|
||||
|
||||
$thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
|
||||
$this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function readHeaderBSI($length) {
|
||||
$data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
|
||||
$this->BSIoffset += $length;
|
||||
|
||||
return bindec($data);
|
||||
}
|
||||
|
||||
public static function AC3sampleRateCodeLookup($fscod) {
|
||||
static $AC3sampleRateCodeLookup = array(
|
||||
0 => 48000,
|
||||
1 => 44100,
|
||||
2 => 32000,
|
||||
3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
|
||||
);
|
||||
return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false);
|
||||
}
|
||||
|
||||
public static function AC3serviceTypeLookup($bsmod, $acmod) {
|
||||
static $AC3serviceTypeLookup = array();
|
||||
if (empty($AC3serviceTypeLookup)) {
|
||||
for ($i = 0; $i <= 7; $i++) {
|
||||
$AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
|
||||
$AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
|
||||
$AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
|
||||
$AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
|
||||
$AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
|
||||
$AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
|
||||
$AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
|
||||
}
|
||||
|
||||
$AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
|
||||
for ($i = 2; $i <= 7; $i++) {
|
||||
$AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke';
|
||||
}
|
||||
}
|
||||
return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false);
|
||||
}
|
||||
|
||||
public static function AC3audioCodingModeLookup($acmod) {
|
||||
static $AC3audioCodingModeLookup = array();
|
||||
if (empty($AC3audioCodingModeLookup)) {
|
||||
// array(channel configuration, # channels (not incl LFE), channel order)
|
||||
$AC3audioCodingModeLookup = array (
|
||||
0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
|
||||
1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
|
||||
2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
|
||||
3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
|
||||
4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
|
||||
5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
|
||||
6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
|
||||
7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR')
|
||||
);
|
||||
}
|
||||
return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false);
|
||||
}
|
||||
|
||||
public static function AC3centerMixLevelLookup($cmixlev) {
|
||||
static $AC3centerMixLevelLookup;
|
||||
if (empty($AC3centerMixLevelLookup)) {
|
||||
$AC3centerMixLevelLookup = array(
|
||||
0 => pow(2, -3.0 / 6), // 0.707 (–3.0 dB)
|
||||
1 => pow(2, -4.5 / 6), // 0.595 (–4.5 dB)
|
||||
2 => pow(2, -6.0 / 6), // 0.500 (–6.0 dB)
|
||||
3 => 'reserved'
|
||||
);
|
||||
}
|
||||
return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false);
|
||||
}
|
||||
|
||||
public static function AC3surroundMixLevelLookup($surmixlev) {
|
||||
static $AC3surroundMixLevelLookup;
|
||||
if (empty($AC3surroundMixLevelLookup)) {
|
||||
$AC3surroundMixLevelLookup = array(
|
||||
0 => pow(2, -3.0 / 6),
|
||||
1 => pow(2, -6.0 / 6),
|
||||
2 => 0,
|
||||
3 => 'reserved'
|
||||
);
|
||||
}
|
||||
return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false);
|
||||
}
|
||||
|
||||
public static function AC3dolbySurroundModeLookup($dsurmod) {
|
||||
static $AC3dolbySurroundModeLookup = array(
|
||||
0 => 'not indicated',
|
||||
1 => 'Not Dolby Surround encoded',
|
||||
2 => 'Dolby Surround encoded',
|
||||
3 => 'reserved'
|
||||
);
|
||||
return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false);
|
||||
}
|
||||
|
||||
public static function AC3channelsEnabledLookup($acmod, $lfeon) {
|
||||
$AC3channelsEnabledLookup = array(
|
||||
'ch1'=>(bool) ($acmod == 0),
|
||||
'ch2'=>(bool) ($acmod == 0),
|
||||
'left'=>(bool) ($acmod > 1),
|
||||
'right'=>(bool) ($acmod > 1),
|
||||
'center'=>(bool) ($acmod & 0x01),
|
||||
'surround_mono'=>false,
|
||||
'surround_left'=>false,
|
||||
'surround_right'=>false,
|
||||
'lfe'=>$lfeon);
|
||||
switch ($acmod) {
|
||||
case 4:
|
||||
case 5:
|
||||
$AC3channelsEnabledLookup['surround_mono'] = true;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
$AC3channelsEnabledLookup['surround_left'] = true;
|
||||
$AC3channelsEnabledLookup['surround_right'] = true;
|
||||
break;
|
||||
}
|
||||
return $AC3channelsEnabledLookup;
|
||||
}
|
||||
|
||||
public static function AC3heavyCompression($compre) {
|
||||
// The first four bits indicate gain changes in 6.02dB increments which can be
|
||||
// implemented with an arithmetic shift operation. The following four bits
|
||||
// indicate linear gain changes, and require a 5-bit multiply.
|
||||
// We will represent the two 4-bit fields of compr as follows:
|
||||
// X0 X1 X2 X3 . Y4 Y5 Y6 Y7
|
||||
// The meaning of the X values is most simply described by considering X to represent a 4-bit
|
||||
// signed integer with values from –8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
|
||||
// following table shows this in detail.
|
||||
|
||||
// Meaning of 4 msb of compr
|
||||
// 7 +48.16 dB
|
||||
// 6 +42.14 dB
|
||||
// 5 +36.12 dB
|
||||
// 4 +30.10 dB
|
||||
// 3 +24.08 dB
|
||||
// 2 +18.06 dB
|
||||
// 1 +12.04 dB
|
||||
// 0 +6.02 dB
|
||||
// -1 0 dB
|
||||
// -2 –6.02 dB
|
||||
// -3 –12.04 dB
|
||||
// -4 –18.06 dB
|
||||
// -5 –24.08 dB
|
||||
// -6 –30.10 dB
|
||||
// -7 –36.12 dB
|
||||
// -8 –42.14 dB
|
||||
|
||||
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
|
||||
if ($fourbit{0} == '1') {
|
||||
$log_gain = -8 + bindec(substr($fourbit, 1));
|
||||
} else {
|
||||
$log_gain = bindec(substr($fourbit, 1));
|
||||
}
|
||||
$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
|
||||
|
||||
// The value of Y is a linear representation of a gain change of up to –6 dB. Y is considered to
|
||||
// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
|
||||
// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
|
||||
// changes from –0.28 dB to –6.02 dB.
|
||||
|
||||
$lin_gain = (16 + ($compre & 0x0F)) / 32;
|
||||
|
||||
// The combination of X and Y values allows compr to indicate gain changes from
|
||||
// 48.16 – 0.28 = +47.89 dB, to
|
||||
// –42.14 – 6.02 = –48.16 dB.
|
||||
|
||||
return $log_gain - $lin_gain;
|
||||
}
|
||||
|
||||
public static function AC3roomTypeLookup($roomtyp) {
|
||||
static $AC3roomTypeLookup = array(
|
||||
0 => 'not indicated',
|
||||
1 => 'large room, X curve monitor',
|
||||
2 => 'small room, flat monitor',
|
||||
3 => 'reserved'
|
||||
);
|
||||
return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false);
|
||||
}
|
||||
|
||||
public static function AC3frameSizeLookup($frmsizecod, $fscod) {
|
||||
$padding = (bool) ($frmsizecod % 2);
|
||||
$framesizeid = floor($frmsizecod / 2);
|
||||
|
||||
static $AC3frameSizeLookup = array();
|
||||
if (empty($AC3frameSizeLookup)) {
|
||||
$AC3frameSizeLookup = array (
|
||||
0 => array(128, 138, 192),
|
||||
1 => array(40, 160, 174, 240),
|
||||
2 => array(48, 192, 208, 288),
|
||||
3 => array(56, 224, 242, 336),
|
||||
4 => array(64, 256, 278, 384),
|
||||
5 => array(80, 320, 348, 480),
|
||||
6 => array(96, 384, 416, 576),
|
||||
7 => array(112, 448, 486, 672),
|
||||
8 => array(128, 512, 556, 768),
|
||||
9 => array(160, 640, 696, 960),
|
||||
10 => array(192, 768, 834, 1152),
|
||||
11 => array(224, 896, 974, 1344),
|
||||
12 => array(256, 1024, 1114, 1536),
|
||||
13 => array(320, 1280, 1392, 1920),
|
||||
14 => array(384, 1536, 1670, 2304),
|
||||
15 => array(448, 1792, 1950, 2688),
|
||||
16 => array(512, 2048, 2228, 3072),
|
||||
17 => array(576, 2304, 2506, 3456),
|
||||
18 => array(640, 2560, 2786, 3840)
|
||||
);
|
||||
}
|
||||
if (($fscod == 1) && $padding) {
|
||||
// frame lengths are padded by 1 word (16 bits) at 44100
|
||||
$AC3frameSizeLookup[$frmsizecod] += 2;
|
||||
}
|
||||
return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false);
|
||||
}
|
||||
|
||||
public static function AC3bitrateLookup($frmsizecod) {
|
||||
$framesizeid = floor($frmsizecod / 2);
|
||||
|
||||
static $AC3bitrateLookup = array(
|
||||
0 => 32000,
|
||||
1 => 40000,
|
||||
2 => 48000,
|
||||
3 => 56000,
|
||||
4 => 64000,
|
||||
5 => 80000,
|
||||
6 => 96000,
|
||||
7 => 112000,
|
||||
8 => 128000,
|
||||
9 => 160000,
|
||||
10 => 192000,
|
||||
11 => 224000,
|
||||
12 => 256000,
|
||||
13 => 320000,
|
||||
14 => 384000,
|
||||
15 => 448000,
|
||||
16 => 512000,
|
||||
17 => 576000,
|
||||
18 => 640000
|
||||
);
|
||||
return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?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.audio.avr.php //
|
||||
// module for analyzing AVR Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_avr extends getid3_handler
|
||||
{
|
||||
|
||||
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
|
||||
// offset type length name comments
|
||||
// ---------------------------------------------------------------------
|
||||
// 0 char 4 ID format ID == "2BIT"
|
||||
// 4 char 8 name sample name (unused space filled with 0)
|
||||
// 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo
|
||||
// With stereo, samples are alternated,
|
||||
// the first voice is the left :
|
||||
// (LRLRLRLRLRLRLRLRLR...)
|
||||
// 14 short 1 resolution 8, 12 or 16 (bits)
|
||||
// 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed
|
||||
// 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on
|
||||
// 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127
|
||||
// 0xFFFF means "no MIDI note defined"
|
||||
// 22 byte 1 Replay speed Frequence in the Replay software
|
||||
// 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
|
||||
// 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
|
||||
// 6=43.885 Khz, 7=47.261 Khz
|
||||
// -1 (0xFF)=no defined Frequence
|
||||
// 23 byte 3 sample rate in Hertz
|
||||
// 26 long 1 size in bytes (2 * bytes in stereo)
|
||||
// 30 long 1 loop begin 0 for no loop
|
||||
// 34 long 1 loop size equal to 'size' for no loop
|
||||
// 38 short 2 Reserved, MIDI keyboard split */
|
||||
// 40 short 2 Reserved, sample compression */
|
||||
// 42 short 2 Reserved */
|
||||
// 44 char 20; Additional filename space, used if (name[7] != 0)
|
||||
// 64 byte 64 user data
|
||||
// 128 bytes ? sample data (12 bits samples are coded on 16 bits:
|
||||
// 0000 xxxx xxxx xxxx)
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Note that all values are in motorola (big-endian) format, and that long is
|
||||
// assumed to be 4 bytes, and short 2 bytes.
|
||||
// When reading the samples, you should handle both signed and unsigned data,
|
||||
// and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
|
||||
// 8-bit data between signed/unsigned just add 127 to the sample values.
|
||||
// Simularly for 16-bit data you should add 32769
|
||||
|
||||
$info['fileformat'] = 'avr';
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$AVRheader = fread($this->getid3->fp, 128);
|
||||
|
||||
$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;
|
||||
}
|
||||
$info['avdataoffset'] += 128;
|
||||
|
||||
$info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
|
||||
$info['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
|
||||
$info['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
|
||||
$info['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
|
||||
$info['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
|
||||
$info['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
|
||||
$info['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
|
||||
$info['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
|
||||
$info['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
|
||||
$info['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
|
||||
$info['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
|
||||
$info['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
|
||||
$info['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
|
||||
$info['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
|
||||
$info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
|
||||
$info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
|
||||
|
||||
$info['avr']['flags']['stereo'] = (($info['avr']['raw']['mono'] == 0) ? false : true);
|
||||
$info['avr']['flags']['signed'] = (($info['avr']['raw']['signed'] == 0) ? false : true);
|
||||
$info['avr']['flags']['loop'] = (($info['avr']['raw']['loop'] == 0) ? false : true);
|
||||
|
||||
$info['avr']['midi_notes'] = array();
|
||||
if (($info['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
|
||||
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xFF00) >> 8;
|
||||
}
|
||||
if (($info['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
|
||||
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0x00FF);
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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 ($thisfile_bonk['dataend'] >= pow(2, 31)) {
|
||||
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 2GB';
|
||||
$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) {
|
|
@ -7,50 +7,53 @@
|
|||
// See readme.txt for more details //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio.au.php //
|
||||
// module.audio.dss.php //
|
||||
// module for analyzing Digital Speech Standard (DSS) files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
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 (substr($DSSheader, 0, 4) != "\x02".'dss') {
|
||||
$ThisFileInfo['error'][] = 'Expecting "[x02]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));
|
||||
$thisfile_dss['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
|
||||
$thisfile_dss['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
|
||||
$thisfile_dss['length'] = intval(substr($DSSheader, 62, 6));
|
||||
//$thisfile_dss['length'] = intval(substr($DSSheader, 62, 6)); // I thought time was in seconds, it's actually HHMMSS
|
||||
$thisfile_dss['length'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2));
|
||||
$thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1));
|
||||
$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;
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
<?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.audio.dts.php //
|
||||
// module for analyzing DTS Audio files //
|
||||
// dependencies: NONE //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_dts extends getid3_handler
|
||||
{
|
||||
|
||||
public 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/
|
||||
|
||||
$info['fileformat'] = 'dts';
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$DTSheader = fread($this->getid3->fp, 16);
|
||||
$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'], $info['dts']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
|
||||
$bsOffset = 0;
|
||||
$info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, $bsOffset, 5);
|
||||
$info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, $bsOffset, 7);
|
||||
$info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, $bsOffset, 14);
|
||||
$info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, $bsOffset, 6);
|
||||
$info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, $bsOffset, 4);
|
||||
$info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, $bsOffset, 5);
|
||||
$info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, $bsOffset, 3);
|
||||
$info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, $bsOffset, 2);
|
||||
$info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
if ($info['dts']['flags']['crc_present']) {
|
||||
$info['dts']['raw']['crc16'] = $this->readBinData($fhBS, $bsOffset, 16);
|
||||
}
|
||||
$info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, $bsOffset, 4);
|
||||
$info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, $bsOffset, 2);
|
||||
$info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, $bsOffset, 2);
|
||||
$info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
|
||||
$info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, $bsOffset, 4);
|
||||
|
||||
|
||||
$info['dts']['bitrate'] = self::DTSbitrateLookup($info['dts']['raw']['bitrate']);
|
||||
$info['dts']['bits_per_sample'] = self::DTSbitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
|
||||
$info['dts']['sample_rate'] = self::DTSsampleRateLookup($info['dts']['raw']['sample_frequency']);
|
||||
$info['dts']['dialog_normalization'] = self::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'] = self::DTSnumChannelsLookup($info['dts']['raw']['channel_arrangement']);
|
||||
$info['dts']['channel_arrangement'] = self::DTSchannelArrangementLookup($info['dts']['raw']['channel_arrangement']);
|
||||
|
||||
$info['audio']['dataformat'] = 'dts';
|
||||
$info['audio']['lossless'] = $info['dts']['flags']['lossless'];
|
||||
$info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
|
||||
$info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
|
||||
$info['audio']['sample_rate'] = $info['dts']['sample_rate'];
|
||||
$info['audio']['channels'] = $info['dts']['channels'];
|
||||
$info['audio']['bitrate'] = $info['dts']['bitrate'];
|
||||
if (isset($info['avdataend'])) {
|
||||
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function readBinData($bin, &$offset, $length) {
|
||||
$data = substr($bin, $offset, $length);
|
||||
$offset += $length;
|
||||
return bindec($data);
|
||||
}
|
||||
|
||||
private static function DTSbitrateLookup($index) {
|
||||
$DTSbitrateLookup = array(
|
||||
0 => 32000,
|
||||
1 => 56000,
|
||||
2 => 64000,
|
||||
3 => 96000,
|
||||
4 => 112000,
|
||||
5 => 128000,
|
||||
6 => 192000,
|
||||
7 => 224000,
|
||||
8 => 256000,
|
||||
9 => 320000,
|
||||
10 => 384000,
|
||||
11 => 448000,
|
||||
12 => 512000,
|
||||
13 => 576000,
|
||||
14 => 640000,
|
||||
15 => 768000,
|
||||
16 => 960000,
|
||||
17 => 1024000,
|
||||
18 => 1152000,
|
||||
19 => 1280000,
|
||||
20 => 1344000,
|
||||
21 => 1408000,
|
||||
22 => 1411200,
|
||||
23 => 1472000,
|
||||
24 => 1536000,
|
||||
25 => 1920000,
|
||||
26 => 2048000,
|
||||
27 => 3072000,
|
||||
28 => 3840000,
|
||||
29 => 'open',
|
||||
30 => 'variable',
|
||||
31 => 'lossless'
|
||||
);
|
||||
return (isset($DTSbitrateLookup[$index]) ? $DTSbitrateLookup[$index] : false);
|
||||
}
|
||||
|
||||
private static function DTSsampleRateLookup($index) {
|
||||
$DTSsampleRateLookup = array(
|
||||
0 => 'invalid',
|
||||
1 => 8000,
|
||||
2 => 16000,
|
||||
3 => 32000,
|
||||
4 => 'invalid',
|
||||
5 => 'invalid',
|
||||
6 => 11025,
|
||||
7 => 22050,
|
||||
8 => 44100,
|
||||
9 => 'invalid',
|
||||
10 => 'invalid',
|
||||
11 => 12000,
|
||||
12 => 24000,
|
||||
13 => 48000,
|
||||
14 => 'invalid',
|
||||
15 => 'invalid'
|
||||
);
|
||||
return (isset($DTSsampleRateLookup[$index]) ? $DTSsampleRateLookup[$index] : false);
|
||||
}
|
||||
|
||||
private static function DTSbitPerSampleLookup($index) {
|
||||
$DTSbitPerSampleLookup = array(
|
||||
0 => 16,
|
||||
1 => 20,
|
||||
2 => 24,
|
||||
3 => 24,
|
||||
);
|
||||
return (isset($DTSbitPerSampleLookup[$index]) ? $DTSbitPerSampleLookup[$index] : false);
|
||||
}
|
||||
|
||||
private static function DTSnumChannelsLookup($index) {
|
||||
switch ($index) {
|
||||
case 0:
|
||||
return 1;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return 2;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
return 3;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
return 4;
|
||||
break;
|
||||
case 9:
|
||||
return 5;
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
return 6;
|
||||
break;
|
||||
case 13:
|
||||
return 7;
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
return 8;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function DTSchannelArrangementLookup($index) {
|
||||
$DTSchannelArrangementLookup = array(
|
||||
0 => 'A',
|
||||
1 => 'A + B (dual mono)',
|
||||
2 => 'L + R (stereo)',
|
||||
3 => '(L+R) + (L-R) (sum-difference)',
|
||||
4 => 'LT + RT (left and right total)',
|
||||
5 => 'C + L + R',
|
||||
6 => 'L + R + S',
|
||||
7 => 'C + L + R + S',
|
||||
8 => 'L + R + SL + SR',
|
||||
9 => 'C + L + R + SL + SR',
|
||||
10 => 'CL + CR + L + R + SL + SR',
|
||||
11 => 'C + L + R+ LR + RR + OV',
|
||||
12 => 'CF + CR + LF + RF + LR + RR',
|
||||
13 => 'CL + C + CR + L + R + SL + SR',
|
||||
14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
|
||||
15 => 'CL + C+ CR + L + R + SL + S + SR',
|
||||
);
|
||||
return (isset($DTSchannelArrangementLookup[$index]) ? $DTSchannelArrangementLookup[$index] : 'user-defined');
|
||||
}
|
||||
|
||||
private static function DTSdialogNormalization($index, $version) {
|
||||
switch ($version) {
|
||||
case 7:
|
||||
return 0 - $index;
|
||||
break;
|
||||
case 6:
|
||||
return 0 - 16 - $index;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,480 @@
|
|||
<?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.audio.flac.php //
|
||||
// module for analyzing FLAC and OggFLAC audio files //
|
||||
// dependencies: module.audio.ogg.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
|
||||
|
||||
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;
|
||||
|
||||
// http://flac.sourceforge.net/format.html
|
||||
|
||||
$this->fseek($info['avdataoffset'], SEEK_SET);
|
||||
$StreamMarker = $this->fread(4);
|
||||
$magic = 'fLaC';
|
||||
if ($StreamMarker != $magic) {
|
||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"';
|
||||
return false;
|
||||
}
|
||||
$info['fileformat'] = 'flac';
|
||||
$info['audio']['dataformat'] = 'flac';
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
$info['audio']['lossless'] = true;
|
||||
|
||||
return $this->FLACparseMETAdata();
|
||||
}
|
||||
|
||||
|
||||
function FLACparseMETAdata() {
|
||||
$info = &$this->getid3->info;
|
||||
do {
|
||||
$METAdataBlockOffset = $this->ftell();
|
||||
$METAdataBlockHeader = $this->fread(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) {
|
||||
$info['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
$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;
|
||||
if (($METAdataBlockOffset + 4 + $METAdataBlockLength) > $info['avdataend']) {
|
||||
$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'] = $this->fread($METAdataBlockLength);
|
||||
$info['avdataoffset'] = $this->ftell();
|
||||
|
||||
switch ($METAdataBlockTypeText) {
|
||||
case 'STREAMINFO': // 0x00
|
||||
if (!$this->FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PADDING': // 0x01
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case 'APPLICATION': // 0x02
|
||||
if (!$this->FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SEEKTABLE': // 0x03
|
||||
if (!$this->FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VORBIS_COMMENT': // 0x04
|
||||
$getid3_temp = new getID3();
|
||||
$getid3_temp->openfile($this->getid3->filename);
|
||||
$getid3_temp->info['avdataoffset'] = $this->ftell() - $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'])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PICTURE': // 0x06
|
||||
if (!getid3_flac::FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$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($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;
|
||||
}
|
||||
$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($info['flac']['STREAMINFO']['audio_signature'])) {
|
||||
|
||||
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
|
||||
|
||||
$info['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
|
||||
|
||||
} else {
|
||||
|
||||
$info['md5_data_source'] = '';
|
||||
$md5 = $info['flac']['STREAMINFO']['audio_signature'];
|
||||
for ($i = 0; $i < strlen($md5); $i++) {
|
||||
$info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
|
||||
}
|
||||
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
|
||||
unset($info['md5_data_source']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$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
|
||||
$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($info['ogg']['vendor'])) {
|
||||
$info['audio']['encoder'] = $info['ogg']['vendor'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function FLACmetaBlockTypeLookup($blocktype) {
|
||||
static $FLACmetaBlockTypeLookup = array();
|
||||
if (empty($FLACmetaBlockTypeLookup)) {
|
||||
$FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
|
||||
$FLACmetaBlockTypeLookup[1] = 'PADDING';
|
||||
$FLACmetaBlockTypeLookup[2] = 'APPLICATION';
|
||||
$FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
|
||||
$FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
|
||||
$FLACmetaBlockTypeLookup[5] = 'CUESHEET';
|
||||
$FLACmetaBlockTypeLookup[6] = 'PICTURE';
|
||||
}
|
||||
return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
|
||||
}
|
||||
|
||||
static function FLACapplicationIDLookup($applicationid) {
|
||||
static $FLACapplicationIDLookup = array();
|
||||
if (empty($FLACapplicationIDLookup)) {
|
||||
// http://flac.sourceforge.net/id.html
|
||||
$FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol'
|
||||
$FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
|
||||
}
|
||||
return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
|
||||
}
|
||||
|
||||
static function FLACpictureTypeLookup($type_id) {
|
||||
static $lookup = array (
|
||||
0 => 'Other',
|
||||
1 => '32x32 pixels \'file icon\' (PNG only)',
|
||||
2 => 'Other file icon',
|
||||
3 => 'Cover (front)',
|
||||
4 => 'Cover (back)',
|
||||
5 => 'Leaflet page',
|
||||
6 => 'Media (e.g. label side of CD)',
|
||||
7 => 'Lead artist/lead performer/soloist',
|
||||
8 => 'Artist/performer',
|
||||
9 => 'Conductor',
|
||||
10 => 'Band/Orchestra',
|
||||
11 => 'Composer',
|
||||
12 => 'Lyricist/text writer',
|
||||
13 => 'Recording Location',
|
||||
14 => 'During recording',
|
||||
15 => 'During performance',
|
||||
16 => 'Movie/video screen capture',
|
||||
17 => 'A bright coloured fish',
|
||||
18 => 'Illustration',
|
||||
19 => 'Band/artist logotype',
|
||||
20 => 'Publisher/Studio logotype',
|
||||
);
|
||||
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
|
||||
}
|
||||
|
||||
function FLACparseSTREAMINFO($METAdataBlockData) {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$offset = 0;
|
||||
$info['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
|
||||
$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));
|
||||
$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;
|
||||
|
||||
$info['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
|
||||
$offset += 16;
|
||||
|
||||
if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
|
||||
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
$info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate'];
|
||||
$info['audio']['channels'] = $info['flac']['STREAMINFO']['channels'];
|
||||
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
|
||||
$info['playtime_seconds'] = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
|
||||
if ($info['playtime_seconds'] > 0) {
|
||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||
}
|
||||
|
||||
} else {
|
||||
$info['error'][] = 'Corrupt METAdata block: STREAMINFO';
|
||||
return false;
|
||||
}
|
||||
unset($info['flac']['STREAMINFO']['raw']);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLACparseAPPLICATION($METAdataBlockData) {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$offset = 0;
|
||||
$ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
|
||||
$info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
|
||||
$offset = $METAdataBlockLength;
|
||||
|
||||
unset($info['flac']['APPLICATION']['raw']);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLACparseSEEKTABLE($METAdataBlockData) {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$offset = 0;
|
||||
$METAdataBlockLength = strlen($METAdataBlockData);
|
||||
$placeholderpattern = str_repeat("\xFF", 8);
|
||||
while ($offset < $METAdataBlockLength) {
|
||||
$SampleNumberString = substr($METAdataBlockData, $offset, 8);
|
||||
$offset += 8;
|
||||
if ($SampleNumberString == $placeholderpattern) {
|
||||
|
||||
// placeholder point
|
||||
getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
|
||||
$offset += 10;
|
||||
|
||||
} else {
|
||||
|
||||
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
|
||||
$info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unset($info['flac']['SEEKTABLE']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function FLACparseCUESHEET($METAdataBlockData) {
|
||||
$info = &$this->getid3->info;
|
||||
$offset = 0;
|
||||
$info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0");
|
||||
$offset += 128;
|
||||
$info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
|
||||
$offset += 8;
|
||||
$info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
|
||||
$offset += 1;
|
||||
|
||||
$offset += 258; // reserved
|
||||
|
||||
$info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
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;
|
||||
|
||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
|
||||
|
||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12);
|
||||
$offset += 12;
|
||||
|
||||
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
$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
|
||||
|
||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
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));
|
||||
$offset += 1;
|
||||
|
||||
$offset += 3; // reserved
|
||||
|
||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
|
||||
}
|
||||
}
|
||||
|
||||
unset($info['flac']['CUESHEET']['raw']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
$picture['typeid'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$picture['type'] = getid3_flac::FLACpictureTypeLookup($picture['typeid']);
|
||||
$offset += 4;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['image_mime'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['description'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
|
||||
$picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$picture['data'] = substr($meta_data_block_data, $offset, $length);
|
||||
$offset += $length;
|
||||
$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);
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,229 @@
|
|||
<?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.audio.la.php //
|
||||
// module for analyzing LA (LosslessAudio) audio files //
|
||||
// dependencies: module.audio.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_la extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$offset = 0;
|
||||
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':
|
||||
$info['fileformat'] = 'la';
|
||||
$info['audio']['dataformat'] = 'la';
|
||||
$info['audio']['lossless'] = true;
|
||||
|
||||
$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;
|
||||
|
||||
$info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
if ($info['la']['uncompressed_size'] == 0) {
|
||||
$info['error'][] = 'Corrupt LA file: uncompressed_size == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$WAVEchunk = substr($rawdata, $offset, 4);
|
||||
if ($WAVEchunk !== 'WAVE') {
|
||||
$info['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
|
||||
return false;
|
||||
}
|
||||
$offset += 4;
|
||||
|
||||
$info['la']['fmt_size'] = 24;
|
||||
if ($info['la']['version'] >= 0.3) {
|
||||
|
||||
$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
|
||||
$info['la']['header_size'] = 41;
|
||||
|
||||
}
|
||||
|
||||
$fmt_chunk = substr($rawdata, $offset, 4);
|
||||
if ($fmt_chunk !== 'fmt ') {
|
||||
$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;
|
||||
|
||||
$info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$info['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
if ($info['la']['channels'] == 0) {
|
||||
$info['error'][] = 'Corrupt LA file: channels == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
if ($info['la']['sample_rate'] == 0) {
|
||||
$info['error'][] = 'Corrupt LA file: sample_rate == zero';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
$info['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||
$offset += 2;
|
||||
|
||||
$info['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
|
||||
$offset += 1;
|
||||
$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);
|
||||
}
|
||||
|
||||
$info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
// mikeØbevin*de
|
||||
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
|
||||
// in earlier versions. A seekpoint is added every blocksize * seekevery
|
||||
// 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 ($info['la']['version'] >= 0.4) {
|
||||
$info['la']['blocksize'] = 61440;
|
||||
$info['la']['seekevery'] = 19;
|
||||
} else {
|
||||
$info['la']['blocksize'] = 73728;
|
||||
$info['la']['seekevery'] = 16;
|
||||
}
|
||||
|
||||
$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 < $info['la']['seekpoint_count']; $i++) {
|
||||
$info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
$info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
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
|
||||
$info['la']['footerstart'] = $info['avdataend'];
|
||||
|
||||
}
|
||||
|
||||
if ($info['la']['footerstart'] < $info['avdataend']) {
|
||||
if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
|
||||
if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
|
||||
$RIFFdata = 'WAVE';
|
||||
if ($info['la']['version'] == 0.2) {
|
||||
$RIFFdata .= substr($rawdata, 12, 24);
|
||||
} else {
|
||||
$RIFFdata .= substr($rawdata, 16, 24);
|
||||
}
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// $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;
|
||||
|
||||
//$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;
|
||||
}
|
||||
|
||||
$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') {
|
||||
$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 {
|
||||
$info['error'][] = 'Not a LA (Lossless-Audio) file';
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,130 @@
|
|||
<?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.audio.lpac.php //
|
||||
// module for analyzing LPAC Audio files //
|
||||
// dependencies: module.audio-video.riff.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
class getid3_lpac extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$LPACheader = fread($this->getid3->fp, 14);
|
||||
if (substr($LPACheader, 0, 4) != 'LPAC') {
|
||||
$info['error'][] = 'Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"';
|
||||
return false;
|
||||
}
|
||||
$info['avdataoffset'] += 14;
|
||||
|
||||
$info['fileformat'] = 'lpac';
|
||||
$info['audio']['dataformat'] = 'lpac';
|
||||
$info['audio']['lossless'] = true;
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
|
||||
$flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
|
||||
$info['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
|
||||
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
|
||||
|
||||
$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 ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
|
||||
$info['warning'][] = '24-bit and 16-bit flags cannot both be set';
|
||||
}
|
||||
|
||||
$info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
|
||||
$info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
|
||||
$info['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
|
||||
$info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
|
||||
$info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
|
||||
$info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
|
||||
$info['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
|
||||
$info['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
|
||||
|
||||
if ($info['lpac']['flags']['fast_compress'] && ($info['lpac']['max_prediction_order'] != 3)) {
|
||||
$info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info['lpac']['max_prediction_order'].'"';
|
||||
}
|
||||
switch ($info['lpac']['file_version']) {
|
||||
case 6:
|
||||
if ($info['lpac']['flags']['adaptive_quantization']) {
|
||||
$info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
|
||||
}
|
||||
if ($info['lpac']['quantization'] != 20) {
|
||||
$info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$info['lpac']['flags']['Q'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//$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;
|
||||
}
|
||||
|
||||
$getid3_temp = new getID3();
|
||||
$getid3_temp->openfile($this->getid3->filename);
|
||||
$getid3_temp->info = $info;
|
||||
$getid3_riff = new getid3_riff($getid3_temp);
|
||||
$getid3_riff->Analyze();
|
||||
$info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
|
||||
$info['riff'] = $getid3_temp->info['riff'];
|
||||
$info['error'] = $getid3_temp->info['error'];
|
||||
$info['warning'] = $getid3_temp->info['warning'];
|
||||
$info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
|
||||
$info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
|
||||
unset($getid3_temp, $getid3_riff);
|
||||
|
||||
$info['audio']['channels'] = ($info['lpac']['flags']['stereo'] ? 2 : 1);
|
||||
|
||||
if ($info['lpac']['flags']['24_bit']) {
|
||||
$info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
|
||||
} elseif ($info['lpac']['flags']['16_bit']) {
|
||||
$info['audio']['bits_per_sample'] = 16;
|
||||
} else {
|
||||
$info['audio']['bits_per_sample'] = 8;
|
||||
}
|
||||
|
||||
if ($info['lpac']['flags']['fast_compress']) {
|
||||
// fast
|
||||
$info['audio']['encoder_options'] = '-1';
|
||||
} else {
|
||||
switch ($info['lpac']['max_prediction_order']) {
|
||||
case 20: // simple
|
||||
$info['audio']['encoder_options'] = '-2';
|
||||
break;
|
||||
case 30: // medium
|
||||
$info['audio']['encoder_options'] = '-3';
|
||||
break;
|
||||
case 40: // high
|
||||
$info['audio']['encoder_options'] = '-4';
|
||||
break;
|
||||
case 60: // extrahigh
|
||||
$info['audio']['encoder_options'] = '-5';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
|
||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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 (@$ThisFileInfo['playtime_seconds'] > 0) {
|
||||
$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'])) {
|
|
@ -0,0 +1,101 @@
|
|||
<?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.audio.mod.php //
|
||||
// module for analyzing MOD Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_mod extends getid3_handler
|
||||
{
|
||||
|
||||
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() {
|
||||
$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)) {
|
||||
$info['error'][] = 'This is not a known type of MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 'mod';
|
||||
|
||||
$info['error'][] = 'MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getXMheaderFilepointer() {
|
||||
$info = &$this->getid3->info;
|
||||
fseek($this->getid3->fp, $info['avdataoffset']);
|
||||
$FormatID = fread($this->getid3->fp, 15);
|
||||
if (!preg_match('#^Extended Module$#', $FormatID)) {
|
||||
$info['error'][] = 'This is not a known type of XM-MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 'xm';
|
||||
|
||||
$info['error'][] = 'XM-MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getS3MheaderFilepointer() {
|
||||
$info = &$this->getid3->info;
|
||||
fseek($this->getid3->fp, $info['avdataoffset'] + 44);
|
||||
$FormatID = fread($this->getid3->fp, 4);
|
||||
if (!preg_match('#^SCRM$#', $FormatID)) {
|
||||
$info['error'][] = 'This is not a ScreamTracker MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 's3m';
|
||||
|
||||
$info['error'][] = 'ScreamTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
}
|
||||
|
||||
function getITheaderFilepointer() {
|
||||
$info = &$this->getid3->info;
|
||||
fseek($this->getid3->fp, $info['avdataoffset']);
|
||||
$FormatID = fread($this->getid3->fp, 4);
|
||||
if (!preg_match('#^IMPM$#', $FormatID)) {
|
||||
$info['error'][] = 'This is not an ImpulseTracker MOD file';
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 'it';
|
||||
|
||||
$info['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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
|
@ -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 (ereg('^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 (ereg('^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']);
|
||||
$thisfile_mpc_header['size'] = 8;
|
||||
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'];
|
||||
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
||||
$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;
|
||||
}
|
|
@ -0,0 +1,705 @@
|
|||
<?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.audio.ogg.php //
|
||||
// module for analyzing Ogg Vorbis, OggFLAC and Speex files //
|
||||
// dependencies: module.audio.flac.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
|
||||
|
||||
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 Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$info['fileformat'] = 'ogg';
|
||||
|
||||
// Warn about illegal tags - only vorbiscomments are allowed
|
||||
if (isset($info['id3v2'])) {
|
||||
$info['warning'][] = 'Illegal ID3v2 tag present.';
|
||||
}
|
||||
if (isset($info['id3v1'])) {
|
||||
$info['warning'][] = 'Illegal ID3v1 tag present.';
|
||||
}
|
||||
if (isset($info['ape'])) {
|
||||
$info['warning'][] = 'Illegal APE tag present.';
|
||||
}
|
||||
|
||||
|
||||
// Page 1 - Stream Header
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
|
||||
$oggpageinfo = $this->ParseOggPageHeader();
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
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($this->getid3->fp, $oggpageinfo['page_length']);
|
||||
$filedataoffset = 0;
|
||||
|
||||
if (substr($filedata, 0, 4) == 'fLaC') {
|
||||
|
||||
$info['audio']['dataformat'] = 'flac';
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
$info['audio']['lossless'] = true;
|
||||
|
||||
} elseif (substr($filedata, 1, 6) == 'vorbis') {
|
||||
|
||||
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
|
||||
|
||||
} elseif (substr($filedata, 0, 8) == 'Speex ') {
|
||||
|
||||
// http://www.speex.org/manual/node10.html
|
||||
|
||||
$info['audio']['dataformat'] = 'speex';
|
||||
$info['mime_type'] = 'audio/speex';
|
||||
$info['audio']['bitrate_mode'] = 'abr';
|
||||
$info['audio']['lossless'] = false;
|
||||
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
|
||||
$filedataoffset += 8;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
|
||||
$filedataoffset += 20;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
|
||||
$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']);
|
||||
|
||||
$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);
|
||||
|
||||
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 {
|
||||
|
||||
$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;
|
||||
|
||||
switch ($info['audio']['dataformat']) {
|
||||
case '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'
|
||||
|
||||
$this->ParseVorbisCommentsFilepointer();
|
||||
break;
|
||||
|
||||
case 'flac':
|
||||
$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($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
|
||||
$this->ParseVorbisCommentsFilepointer();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Last Page - Number of Samples
|
||||
|
||||
if (!getid3_lib::intValueSupported($info['avdataend'])) {
|
||||
|
||||
$info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
|
||||
|
||||
} else {
|
||||
|
||||
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($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;
|
||||
}
|
||||
if (!empty($info['audio']['sample_rate'])) {
|
||||
$info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
|
||||
if ($info['audio']['bitrate'] == 0) {
|
||||
$info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
|
||||
return false;
|
||||
}
|
||||
$info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']);
|
||||
}
|
||||
|
||||
if (isset($info['ogg']['vendor'])) {
|
||||
$info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
|
||||
|
||||
// Vorbis only
|
||||
if ($info['audio']['dataformat'] == 'vorbis') {
|
||||
|
||||
// Vorbis 1.0 starts with Xiph.Org
|
||||
if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
|
||||
|
||||
if ($info['audio']['bitrate_mode'] == 'abr') {
|
||||
|
||||
// Set -b 128 on abr files
|
||||
$info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000);
|
||||
|
||||
} 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
|
||||
$info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
|
||||
$info = &$this->getid3->info;
|
||||
$info['audio']['dataformat'] = 'vorbis';
|
||||
$info['audio']['lossless'] = false;
|
||||
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
|
||||
$filedataoffset += 6;
|
||||
$info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$info['audio']['channels'] = $info['ogg']['numberofchannels'];
|
||||
$info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
if ($info['ogg']['samplerate'] == 0) {
|
||||
$info['error'][] = 'Corrupt Ogg file: sample rate == zero';
|
||||
return false;
|
||||
}
|
||||
$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;
|
||||
$info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
|
||||
$info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
|
||||
$info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
|
||||
|
||||
$info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
|
||||
if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
|
||||
unset($info['ogg']['bitrate_max']);
|
||||
$info['audio']['bitrate_mode'] = 'abr';
|
||||
}
|
||||
if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
|
||||
unset($info['ogg']['bitrate_nominal']);
|
||||
}
|
||||
if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
|
||||
unset($info['ogg']['bitrate_min']);
|
||||
$info['audio']['bitrate_mode'] = 'abr';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseOggPageHeader() {
|
||||
// http://xiph.org/ogg/vorbis/doc/framing.html
|
||||
$oggheader['page_start_offset'] = ftell($this->getid3->fp); // where we started from in the file
|
||||
|
||||
$filedata = fread($this->getid3->fp, $this->getid3->fread_buffer_size());
|
||||
$filedataoffset = 0;
|
||||
while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
|
||||
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($this->getid3->fp) || (($filedata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size())) === false)) {
|
||||
// get some more data, unless eof, in which case fail
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
|
||||
|
||||
$oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
|
||||
$oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
|
||||
$oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
|
||||
|
||||
$oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
|
||||
$filedataoffset += 8;
|
||||
$oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
|
||||
$filedataoffset += 4;
|
||||
$oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['page_length'] = 0;
|
||||
for ($i = 0; $i < $oggheader['page_segments']; $i++) {
|
||||
$oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
|
||||
$filedataoffset += 1;
|
||||
$oggheader['page_length'] += $oggheader['segment_table'][$i];
|
||||
}
|
||||
$oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
|
||||
$oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
|
||||
fseek($this->getid3->fp, $oggheader['header_end_offset'], SEEK_SET);
|
||||
|
||||
return $oggheader;
|
||||
}
|
||||
|
||||
|
||||
function ParseVorbisCommentsFilepointer() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$OriginalOffset = ftell($this->getid3->fp);
|
||||
$commentdataoffset = 0;
|
||||
$VorbisCommentPage = 1;
|
||||
|
||||
switch ($info['audio']['dataformat']) {
|
||||
case 'vorbis':
|
||||
$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':
|
||||
$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 = $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:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
|
||||
$commentdataoffset += 4;
|
||||
|
||||
$info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
|
||||
$commentdataoffset += $VendorSize;
|
||||
|
||||
$CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
|
||||
$commentdataoffset += 4;
|
||||
$info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
|
||||
|
||||
$basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
|
||||
$ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
|
||||
for ($i = 0; $i < $CommentsCount; $i++) {
|
||||
|
||||
$ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
|
||||
|
||||
if (ftell($this->getid3->fp) < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
|
||||
if ($oggpageinfo = $this->ParseOggPageHeader()) {
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
$VorbisCommentPage++;
|
||||
|
||||
// First, save what we haven't read yet
|
||||
$AsYetUnusedData = substr($commentdata, $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 + $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($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
|
||||
$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'] > $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 = $this->ParseOggPageHeader();
|
||||
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
|
||||
|
||||
// First, save what we haven't read yet
|
||||
$AsYetUnusedData = substr($commentdata, $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 + $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($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($info['ogg']['pageheader'][$VorbisCommentPage], 1);
|
||||
if ($readlength <= 0) {
|
||||
$info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($this->getid3->fp);
|
||||
break;
|
||||
}
|
||||
$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?
|
||||
$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]['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();
|
||||
$imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo_ogg_comments_raw[$i]['data'], $imageinfo);
|
||||
unset($imageinfo);
|
||||
if (!empty($imagechunkcheck)) {
|
||||
$ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
|
||||
if ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] && ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] != 'application/octet-stream')) {
|
||||
unset($ThisFileInfo_ogg_comments_raw[$i]['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($ThisFileInfo_ogg_comments_raw[$i]['value'])) {
|
||||
unset($ThisFileInfo_ogg_comments_raw[$i]['data']);
|
||||
$info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
|
||||
} else {
|
||||
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 {
|
||||
|
||||
$info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Replay Gain Adjustment
|
||||
// http://privatewww.essex.ac.uk/~djmrob/replaygain/
|
||||
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':
|
||||
$info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
|
||||
unset($info['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'rg_radio':
|
||||
case 'replaygain_track_gain':
|
||||
$info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
|
||||
unset($info['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'replaygain_album_peak':
|
||||
$info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
|
||||
unset($info['ogg']['comments'][$index]);
|
||||
break;
|
||||
|
||||
case 'rg_peak':
|
||||
case 'replaygain_track_peak':
|
||||
$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
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this->getid3->fp, $OriginalOffset, SEEK_SET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function SpeexBandModeLookup($mode) {
|
||||
static $SpeexBandModeLookup = array();
|
||||
if (empty($SpeexBandModeLookup)) {
|
||||
$SpeexBandModeLookup[0] = 'narrow';
|
||||
$SpeexBandModeLookup[1] = 'wide';
|
||||
$SpeexBandModeLookup[2] = 'ultra-wide';
|
||||
}
|
||||
return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
|
||||
}
|
||||
|
||||
|
||||
static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
|
||||
for ($i = 0; $i < $SegmentNumber; $i++) {
|
||||
$segmentlength = 0;
|
||||
foreach ($OggInfoArray['segment_table'] as $key => $value) {
|
||||
$segmentlength += $value;
|
||||
if ($value < 255) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $segmentlength;
|
||||
}
|
||||
|
||||
|
||||
static function get_quality_from_nominal_bitrate($nominal_bitrate) {
|
||||
|
||||
// decrease precision
|
||||
$nominal_bitrate = $nominal_bitrate / 1000;
|
||||
|
||||
if ($nominal_bitrate < 128) {
|
||||
// q-1 to q4
|
||||
$qval = ($nominal_bitrate - 64) / 16;
|
||||
} elseif ($nominal_bitrate < 256) {
|
||||
// q4 to q8
|
||||
$qval = $nominal_bitrate / 32;
|
||||
} elseif ($nominal_bitrate < 320) {
|
||||
// q8 to q9
|
||||
$qval = ($nominal_bitrate + 256) / 64;
|
||||
} else {
|
||||
// q9 to q10
|
||||
$qval = ($nominal_bitrate + 1300) / 180;
|
||||
}
|
||||
//return $qval; // 5.031324
|
||||
//return intval($qval); // 5
|
||||
return round($qval, 1); // 5 or 4.9
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?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.audio.shorten.php //
|
||||
// module for analyzing Shorten Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_rkau extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 'rkau';
|
||||
$info['audio']['dataformat'] = 'rkau';
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$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));
|
||||
|
||||
$info['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
|
||||
$this->RKAUqualityLookup($info['rkau']);
|
||||
|
||||
$info['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
|
||||
$info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x01));
|
||||
$info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02);
|
||||
$info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04);
|
||||
|
||||
if ($info['rkau']['flags']['streaming']) {
|
||||
$info['avdataoffset'] += 20;
|
||||
$info['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
|
||||
} else {
|
||||
$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(?)
|
||||
|
||||
$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'];
|
||||
|
||||
$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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function RKAUqualityLookup(&$RKAUdata) {
|
||||
$level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
|
||||
$quality = $RKAUdata['raw']['quality'] & 0x0F;
|
||||
|
||||
$RKAUdata['lossless'] = (($quality == 0) ? true : false);
|
||||
$RKAUdata['compression_level'] = $level + 1;
|
||||
if (!$RKAUdata['lossless']) {
|
||||
$RKAUdata['quality_setting'] = $quality;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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));
|
||||
|
@ -101,16 +104,16 @@ class getid3_shorten
|
|||
// $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||
// $SeekTableOffset += 4;
|
||||
// }
|
||||
//
|
||||
// $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry;
|
||||
//
|
||||
// $info['shn']['seektable']['entries'][] = $SeekTableEntry;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((bool) ini_get('safe_mode')) {
|
||||
$ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
|
||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||
$info['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -119,24 +122,24 @@ 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 {
|
||||
|
||||
static $shorten_present;
|
||||
if (!isset($shorten_present)) {
|
||||
$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';
|
||||
return false;
|
||||
}
|
||||
$commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64';
|
||||
static $shorten_present;
|
||||
if (!isset($shorten_present)) {
|
||||
$shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
|
||||
}
|
||||
if (!$shorten_present) {
|
||||
$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($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;
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?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.audio.tta.php //
|
||||
// module for analyzing TTA Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_tta extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$info['fileformat'] = 'tta';
|
||||
$info['audio']['dataformat'] = 'tta';
|
||||
$info['audio']['lossless'] = true;
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$ttaheader = fread($this->getid3->fp, 26);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
switch ($ttaheader{3}) {
|
||||
case "\x01": // TTA v1.x
|
||||
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."
|
||||
$info['tta']['major_version'] = 1;
|
||||
$info['avdataoffset'] += 16;
|
||||
|
||||
$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));
|
||||
|
||||
$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."
|
||||
$info['tta']['major_version'] = 2;
|
||||
$info['avdataoffset'] += 20;
|
||||
|
||||
$info['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
||||
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
|
||||
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
||||
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
|
||||
|
||||
$info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level'];
|
||||
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
||||
break;
|
||||
|
||||
case '1': // TTA v3.x
|
||||
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
|
||||
$info['tta']['major_version'] = 3;
|
||||
$info['avdataoffset'] += 26;
|
||||
|
||||
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup()
|
||||
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
|
||||
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
|
||||
$info['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
|
||||
$info['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
|
||||
|
||||
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$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;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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,24 +56,24 @@ 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();
|
||||
|
||||
@$thisfile_voc['blocktypes'][$BlockType]++;
|
||||
getid3_lib::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1);
|
||||
switch ($BlockType) {
|
||||
case 0: // Terminator
|
||||
// do nothing, we'll break out of the loop down below
|
||||
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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
<?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.audio.wavpack.php //
|
||||
// module for analyzing WavPack v4.0+ Audio files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_wavpack extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
|
||||
while (true) {
|
||||
|
||||
$wavpackheader = fread($this->getid3->fp, 32);
|
||||
|
||||
if (ftell($this->getid3->fp) >= $info['avdataend']) {
|
||||
break;
|
||||
} elseif (feof($this->getid3->fp)) {
|
||||
break;
|
||||
} elseif (
|
||||
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($this->getid3->fp) - 32;
|
||||
$blockheader_magic = substr($wavpackheader, 0, 4);
|
||||
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
|
||||
|
||||
$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($info['fileformat']);
|
||||
unset($info['audio']);
|
||||
unset($info['wavpack']);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
// total_samples) are not guaranteed to be correct until (block_samples > 0)
|
||||
|
||||
// Finally, I have defined a format for files in which the length is not known
|
||||
// (for example when raw files are created using pipes). In these cases
|
||||
// total_samples will be -1 and you must seek to the final block to determine
|
||||
// the total number of samples.
|
||||
|
||||
|
||||
$info['audio']['dataformat'] = 'wavpack';
|
||||
$info['fileformat'] = 'wavpack';
|
||||
$info['audio']['lossless'] = true;
|
||||
$info['audio']['bitrate_mode'] = 'vbr';
|
||||
|
||||
$info['wavpack']['blockheader']['offset'] = $blockheader_offset;
|
||||
$info['wavpack']['blockheader']['magic'] = $blockheader_magic;
|
||||
$info['wavpack']['blockheader']['size'] = $blockheader_size;
|
||||
|
||||
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($info['fileformat']);
|
||||
unset($info['audio']);
|
||||
unset($info['wavpack']);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
|
||||
$info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
|
||||
|
||||
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($info['fileformat']);
|
||||
unset($info['audio']);
|
||||
unset($info['wavpack']);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$info['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
|
||||
$info['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
|
||||
$info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
|
||||
$info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
|
||||
$info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
|
||||
$info['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
|
||||
$info['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
|
||||
|
||||
$info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x00000003);
|
||||
$info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000004);
|
||||
$info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000008);
|
||||
$info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000010);
|
||||
$info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000020);
|
||||
$info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000040);
|
||||
$info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000080);
|
||||
$info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000100);
|
||||
$info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000200);
|
||||
$info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000400);
|
||||
$info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000800);
|
||||
$info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00001000);
|
||||
|
||||
$info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid'];
|
||||
}
|
||||
|
||||
while (!feof($this->getid3->fp) && (ftell($this->getid3->fp) < ($blockheader_offset + $blockheader_size + 8))) {
|
||||
|
||||
$metablock = array('offset'=>ftell($this->getid3->fp));
|
||||
$metablockheader = fread($this->getid3->fp, 2);
|
||||
if (feof($this->getid3->fp)) {
|
||||
break;
|
||||
}
|
||||
$metablock['id'] = ord($metablockheader{0});
|
||||
$metablock['function_id'] = ($metablock['id'] & 0x3F);
|
||||
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
|
||||
|
||||
// The 0x20 bit in the id of the meta subblocks (which is defined as
|
||||
// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
|
||||
// if a decoder encounters an id that it does not know about, it uses
|
||||
// that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set
|
||||
// then the decoder simply ignores the metadata, but if it is zero
|
||||
// then the decoder should quit because it means that an understanding
|
||||
// of the metadata is required to correctly decode the audio.
|
||||
$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
|
||||
|
||||
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
|
||||
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
|
||||
if ($metablock['large_block']) {
|
||||
$metablockheader .= fread($this->getid3->fp, 2);
|
||||
}
|
||||
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
|
||||
$metablock['data'] = null;
|
||||
|
||||
if ($metablock['size'] > 0) {
|
||||
|
||||
switch ($metablock['function_id']) {
|
||||
case 0x21: // ID_RIFF_HEADER
|
||||
case 0x22: // ID_RIFF_TRAILER
|
||||
case 0x23: // ID_REPLAY_GAIN
|
||||
case 0x24: // ID_CUESHEET
|
||||
case 0x25: // ID_CONFIG_BLOCK
|
||||
case 0x26: // ID_MD5_CHECKSUM
|
||||
$metablock['data'] = fread($this->getid3->fp, $metablock['size']);
|
||||
|
||||
if ($metablock['padded_data']) {
|
||||
// padded to the nearest even byte
|
||||
$metablock['size']--;
|
||||
$metablock['data'] = substr($metablock['data'], 0, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x00: // ID_DUMMY
|
||||
case 0x01: // ID_ENCODER_INFO
|
||||
case 0x02: // ID_DECORR_TERMS
|
||||
case 0x03: // ID_DECORR_WEIGHTS
|
||||
case 0x04: // ID_DECORR_SAMPLES
|
||||
case 0x05: // ID_ENTROPY_VARS
|
||||
case 0x06: // ID_HYBRID_PROFILE
|
||||
case 0x07: // ID_SHAPING_WEIGHTS
|
||||
case 0x08: // ID_FLOAT_INFO
|
||||
case 0x09: // ID_INT32_INFO
|
||||
case 0x0A: // ID_WV_BITSTREAM
|
||||
case 0x0B: // ID_WVC_BITSTREAM
|
||||
case 0x0C: // ID_WVX_BITSTREAM
|
||||
case 0x0D: // ID_CHANNEL_INFO
|
||||
fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
|
||||
break;
|
||||
|
||||
default:
|
||||
$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;
|
||||
}
|
||||
|
||||
switch ($metablock['function_id']) {
|
||||
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_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'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x22: // ID_RIFF_TRAILER
|
||||
$metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
|
||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||
|
||||
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
|
||||
$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']);
|
||||
$info['tags']['riff'] = $metablock['comments'];
|
||||
}
|
||||
unset($getid3_temp, $getid3_riff);
|
||||
break;
|
||||
|
||||
|
||||
case 0x23: // ID_REPLAY_GAIN
|
||||
$info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x24: // ID_CUESHEET
|
||||
$info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
||||
break;
|
||||
|
||||
|
||||
case 0x25: // ID_CONFIG_BLOCK
|
||||
$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
|
||||
|
||||
$metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats
|
||||
$metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode
|
||||
$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
|
||||
$metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
|
||||
$metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
|
||||
$metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample
|
||||
$metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping
|
||||
$metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified
|
||||
$metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified
|
||||
$metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source
|
||||
$metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
|
||||
$metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
|
||||
$metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression
|
||||
$metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode
|
||||
$metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet)
|
||||
$metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
|
||||
$metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
|
||||
$metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode
|
||||
$metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints
|
||||
$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 %
|
||||
|
||||
$info['wavpack']['config_flags'] = $metablock['flags'];
|
||||
|
||||
|
||||
$info['audio']['encoder_options'] = '';
|
||||
if ($info['wavpack']['blockheader']['flags']['hybrid']) {
|
||||
$info['audio']['encoder_options'] .= ' -b???';
|
||||
}
|
||||
$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) {
|
||||
$info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
|
||||
} else {
|
||||
$info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes';
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 0x00: // ID_DUMMY
|
||||
case 0x01: // ID_ENCODER_INFO
|
||||
case 0x02: // ID_DECORR_TERMS
|
||||
case 0x03: // ID_DECORR_WEIGHTS
|
||||
case 0x04: // ID_DECORR_SAMPLES
|
||||
case 0x05: // ID_ENTROPY_VARS
|
||||
case 0x06: // ID_HYBRID_PROFILE
|
||||
case 0x07: // ID_SHAPING_WEIGHTS
|
||||
case 0x08: // ID_FLOAT_INFO
|
||||
case 0x09: // ID_INT32_INFO
|
||||
case 0x0A: // ID_WV_BITSTREAM
|
||||
case 0x0B: // ID_WVC_BITSTREAM
|
||||
case 0x0C: // ID_WVX_BITSTREAM
|
||||
case 0x0D: // ID_CHANNEL_INFO
|
||||
unset($metablock);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (!empty($metablock)) {
|
||||
$info['wavpack']['metablocks'][] = $metablock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$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($info['playtime_seconds'])) {
|
||||
|
||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||
|
||||
} else {
|
||||
|
||||
$info['audio']['dataformat'] = 'wvc';
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function WavPackMetablockNameLookup(&$id) {
|
||||
static $WavPackMetablockNameLookup = array(
|
||||
0x00 => 'Dummy',
|
||||
0x01 => 'Encoder Info',
|
||||
0x02 => 'Decorrelation Terms',
|
||||
0x03 => 'Decorrelation Weights',
|
||||
0x04 => 'Decorrelation Samples',
|
||||
0x05 => 'Entropy Variables',
|
||||
0x06 => 'Hybrid Profile',
|
||||
0x07 => 'Shaping Weights',
|
||||
0x08 => 'Float Info',
|
||||
0x09 => 'Int32 Info',
|
||||
0x0A => 'WV Bitstream',
|
||||
0x0B => 'WVC Bitstream',
|
||||
0x0C => 'WVX Bitstream',
|
||||
0x0D => 'Channel Info',
|
||||
0x21 => 'RIFF header',
|
||||
0x22 => 'RIFF trailer',
|
||||
0x23 => 'Replay Gain',
|
||||
0x24 => 'Cuesheet',
|
||||
0x25 => 'Config Block',
|
||||
0x26 => 'MD5 Checksum',
|
||||
);
|
||||
return (isset($WavPackMetablockNameLookup[$id]) ? $WavPackMetablockNameLookup[$id] : '');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -14,16 +14,19 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
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'];
|
||||
$thisfile_bmp_header = &$thisfile_bmp['header'];
|
||||
$thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
|
||||
// shortcuts
|
||||
$info['bmp']['header']['raw'] = array();
|
||||
$thisfile_bmp = &$info['bmp'];
|
||||
$thisfile_bmp_header = &$thisfile_bmp['header'];
|
||||
$thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
|
||||
|
||||
// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
|
||||
// all versions
|
||||
|
@ -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 */
|
||||
|
@ -162,9 +166,9 @@ class getid3_bmp
|
|||
$thisfile_bmp_header_raw['identifier'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
|
||||
$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,12 +317,13 @@ 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;
|
||||
switch (@$thisfile_bmp_header_raw['compression']) {
|
||||
$thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '');
|
||||
switch ($thisfile_bmp_header_raw['compression']) {
|
||||
|
||||
case 0: // BI_RGB
|
||||
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
|
||||
|
@ -400,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;
|
||||
|
@ -475,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;
|
||||
|
@ -564,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;
|
||||
|
@ -604,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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,184 @@
|
|||
<?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.graphic.gif.php //
|
||||
// module for analyzing GIF Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_gif extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$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;
|
||||
|
||||
$info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
|
||||
$offset += 3;
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
|
||||
$offset += 3;
|
||||
$info['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||
$offset += 2;
|
||||
$info['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$info['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
$info['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||
$offset += 1;
|
||||
|
||||
$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
|
||||
$info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
|
||||
} else {
|
||||
$info['gif']['header']['bits_per_pixel'] = 0;
|
||||
}
|
||||
$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]
|
||||
$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 {
|
||||
$info['gif']['header']['global_color_size'] = 0;
|
||||
}
|
||||
if ($info['gif']['header']['raw']['aspect_ratio'] != 0) {
|
||||
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
|
||||
$info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
|
||||
}
|
||||
|
||||
// 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 < $info['gif']['header']['global_color_size']; $i++) {
|
||||
// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
|
||||
// $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Image Descriptor
|
||||
// while (!feof($this->getid3->fp)) {
|
||||
// $NextBlockTest = fread($this->getid3->fp, 1);
|
||||
// switch ($NextBlockTest) {
|
||||
//
|
||||
// case ',': // ',' - Image separator character
|
||||
//
|
||||
// $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));
|
||||
// $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
|
||||
// $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
|
||||
// $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1));
|
||||
// $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80);
|
||||
// $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40);
|
||||
// $info['gif']['image_descriptor'][] = $ImageDescriptor;
|
||||
//
|
||||
// if ($ImageDescriptor['flags']['use_local_color_map']) {
|
||||
//
|
||||
// $info['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
|
||||
// return true;
|
||||
//
|
||||
// }
|
||||
//echo 'Start of raster data: '.ftell($this->getid3->fp).'<BR>';
|
||||
// $RasterData = array();
|
||||
// $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++) {
|
||||
// $DefaultDataLookupTable[$i] = chr($i);
|
||||
// }
|
||||
// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code
|
||||
// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code
|
||||
//
|
||||
//
|
||||
// $NextValue = $this->GetLSBits($CurrentCodeSize);
|
||||
// echo 'Clear Code: '.$NextValue.'<BR>';
|
||||
//
|
||||
// $NextValue = $this->GetLSBits($CurrentCodeSize);
|
||||
// echo 'First Color: '.$NextValue.'<BR>';
|
||||
//
|
||||
// $Prefix = $NextValue;
|
||||
//$i = 0;
|
||||
// while ($i++ < 20) {
|
||||
// $NextValue = $this->GetLSBits($CurrentCodeSize);
|
||||
// echo $NextValue.'<BR>';
|
||||
// }
|
||||
//return true;
|
||||
// break;
|
||||
//
|
||||
// case '!':
|
||||
// // GIF Extension Block
|
||||
// $ExtensionBlockData = $NextBlockTest.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($this->getid3->fp, $ExtensionBlock['byte_length']);
|
||||
// $info['gif']['extension_blocks'][] = $ExtensionBlock;
|
||||
// break;
|
||||
//
|
||||
// case ';':
|
||||
// $info['gif']['terminator_offset'] = ftell($this->getid3->fp) - 1;
|
||||
// // GIF Terminator
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
//
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function GetLSBits($bits) {
|
||||
static $bitbuffer = '';
|
||||
while (strlen($bitbuffer) < $bits) {
|
||||
$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);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,338 @@
|
|||
<?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.graphic.jpg.php //
|
||||
// module for analyzing JPEG Image files //
|
||||
// dependencies: PHP compiled with --enable-exif (optional) //
|
||||
// module.tag.xmp.php (optional) //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_jpg extends getid3_handler
|
||||
{
|
||||
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$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($this->getid3->fp, $info['filesize']), $imageinfo);
|
||||
|
||||
|
||||
if (isset($imageinfo['APP13'])) {
|
||||
// http://php.net/iptcparse
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
$iptc_parsed = iptcparse($imageinfo['APP13']);
|
||||
if (is_array($iptc_parsed)) {
|
||||
foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
|
||||
list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
|
||||
$iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
|
||||
foreach ($iptc_values as $key => $value) {
|
||||
$IPTCrecordName = $this->IPTCrecordName($iptc_record);
|
||||
$IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey);
|
||||
if (isset($info['iptc'][$IPTCrecordName][$IPTCrecordTagName])) {
|
||||
$info['iptc'][$IPTCrecordName][$IPTCrecordTagName][] = $value;
|
||||
} else {
|
||||
$info['iptc'][$IPTCrecordName][$IPTCrecordTagName] = array($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$returnOK = false;
|
||||
switch ($type) {
|
||||
case IMG_JPG:
|
||||
$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') {
|
||||
$info['jpg']['exif'] = @exif_read_data($info['filenamepath'], '', true, false);
|
||||
} else {
|
||||
$info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")';
|
||||
}
|
||||
} else {
|
||||
$info['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif');
|
||||
}
|
||||
}
|
||||
$returnOK = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL');
|
||||
foreach ($cast_as_appropriate_keys as $exif_key) {
|
||||
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($info['jpg']['exif']['GPS'])) {
|
||||
|
||||
if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) {
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1));
|
||||
}
|
||||
$info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts);
|
||||
}
|
||||
|
||||
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] : '');
|
||||
|
||||
if (function_exists('date_default_timezone_set')) {
|
||||
date_default_timezone_set('UTC');
|
||||
} else {
|
||||
ini_set('date.timezone', 'UTC');
|
||||
}
|
||||
|
||||
$computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
$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($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);
|
||||
}
|
||||
$info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
$info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
|
||||
}
|
||||
|
||||
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($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);
|
||||
$info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$returnOK) {
|
||||
unset($info['fileformat']);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function CastAsAppropriate($value) {
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
} elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
|
||||
return getid3_lib::DecimalizeFraction($value);
|
||||
} elseif (preg_match('#^[0-9]+$#', $value)) {
|
||||
return getid3_lib::CastAsInt($value);
|
||||
} elseif (preg_match('#^[0-9\.]+$#', $value)) {
|
||||
return (float) $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
function IPTCrecordName($iptc_record) {
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
static $IPTCrecordName = array();
|
||||
if (empty($IPTCrecordName)) {
|
||||
$IPTCrecordName = array(
|
||||
1 => 'IPTCEnvelope',
|
||||
2 => 'IPTCApplication',
|
||||
3 => 'IPTCNewsPhoto',
|
||||
7 => 'IPTCPreObjectData',
|
||||
8 => 'IPTCObjectData',
|
||||
9 => 'IPTCPostObjectData',
|
||||
);
|
||||
}
|
||||
return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
|
||||
}
|
||||
|
||||
|
||||
function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
|
||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||
static $IPTCrecordTagName = array();
|
||||
if (empty($IPTCrecordTagName)) {
|
||||
$IPTCrecordTagName = array(
|
||||
1 => array( // IPTC EnvelopeRecord Tags
|
||||
0 => 'EnvelopeRecordVersion',
|
||||
5 => 'Destination',
|
||||
20 => 'FileFormat',
|
||||
22 => 'FileVersion',
|
||||
30 => 'ServiceIdentifier',
|
||||
40 => 'EnvelopeNumber',
|
||||
50 => 'ProductID',
|
||||
60 => 'EnvelopePriority',
|
||||
70 => 'DateSent',
|
||||
80 => 'TimeSent',
|
||||
90 => 'CodedCharacterSet',
|
||||
100 => 'UniqueObjectName',
|
||||
120 => 'ARMIdentifier',
|
||||
122 => 'ARMVersion',
|
||||
),
|
||||
2 => array( // IPTC ApplicationRecord Tags
|
||||
0 => 'ApplicationRecordVersion',
|
||||
3 => 'ObjectTypeReference',
|
||||
4 => 'ObjectAttributeReference',
|
||||
5 => 'ObjectName',
|
||||
7 => 'EditStatus',
|
||||
8 => 'EditorialUpdate',
|
||||
10 => 'Urgency',
|
||||
12 => 'SubjectReference',
|
||||
15 => 'Category',
|
||||
20 => 'SupplementalCategories',
|
||||
22 => 'FixtureIdentifier',
|
||||
25 => 'Keywords',
|
||||
26 => 'ContentLocationCode',
|
||||
27 => 'ContentLocationName',
|
||||
30 => 'ReleaseDate',
|
||||
35 => 'ReleaseTime',
|
||||
37 => 'ExpirationDate',
|
||||
38 => 'ExpirationTime',
|
||||
40 => 'SpecialInstructions',
|
||||
42 => 'ActionAdvised',
|
||||
45 => 'ReferenceService',
|
||||
47 => 'ReferenceDate',
|
||||
50 => 'ReferenceNumber',
|
||||
55 => 'DateCreated',
|
||||
60 => 'TimeCreated',
|
||||
62 => 'DigitalCreationDate',
|
||||
63 => 'DigitalCreationTime',
|
||||
65 => 'OriginatingProgram',
|
||||
70 => 'ProgramVersion',
|
||||
75 => 'ObjectCycle',
|
||||
80 => 'By-line',
|
||||
85 => 'By-lineTitle',
|
||||
90 => 'City',
|
||||
92 => 'Sub-location',
|
||||
95 => 'Province-State',
|
||||
100 => 'Country-PrimaryLocationCode',
|
||||
101 => 'Country-PrimaryLocationName',
|
||||
103 => 'OriginalTransmissionReference',
|
||||
105 => 'Headline',
|
||||
110 => 'Credit',
|
||||
115 => 'Source',
|
||||
116 => 'CopyrightNotice',
|
||||
118 => 'Contact',
|
||||
120 => 'Caption-Abstract',
|
||||
121 => 'LocalCaption',
|
||||
122 => 'Writer-Editor',
|
||||
125 => 'RasterizedCaption',
|
||||
130 => 'ImageType',
|
||||
131 => 'ImageOrientation',
|
||||
135 => 'LanguageIdentifier',
|
||||
150 => 'AudioType',
|
||||
151 => 'AudioSamplingRate',
|
||||
152 => 'AudioSamplingResolution',
|
||||
153 => 'AudioDuration',
|
||||
154 => 'AudioOutcue',
|
||||
184 => 'JobID',
|
||||
185 => 'MasterDocumentID',
|
||||
186 => 'ShortDocumentID',
|
||||
187 => 'UniqueDocumentID',
|
||||
188 => 'OwnerID',
|
||||
200 => 'ObjectPreviewFileFormat',
|
||||
201 => 'ObjectPreviewFileVersion',
|
||||
202 => 'ObjectPreviewData',
|
||||
221 => 'Prefs',
|
||||
225 => 'ClassifyState',
|
||||
228 => 'SimilarityIndex',
|
||||
230 => 'DocumentNotes',
|
||||
231 => 'DocumentHistory',
|
||||
232 => 'ExifCameraInfo',
|
||||
),
|
||||
3 => array( // IPTC NewsPhoto Tags
|
||||
0 => 'NewsPhotoVersion',
|
||||
10 => 'IPTCPictureNumber',
|
||||
20 => 'IPTCImageWidth',
|
||||
30 => 'IPTCImageHeight',
|
||||
40 => 'IPTCPixelWidth',
|
||||
50 => 'IPTCPixelHeight',
|
||||
55 => 'SupplementalType',
|
||||
60 => 'ColorRepresentation',
|
||||
64 => 'InterchangeColorSpace',
|
||||
65 => 'ColorSequence',
|
||||
66 => 'ICC_Profile',
|
||||
70 => 'ColorCalibrationMatrix',
|
||||
80 => 'LookupTable',
|
||||
84 => 'NumIndexEntries',
|
||||
85 => 'ColorPalette',
|
||||
86 => 'IPTCBitsPerSample',
|
||||
90 => 'SampleStructure',
|
||||
100 => 'ScanningDirection',
|
||||
102 => 'IPTCImageRotation',
|
||||
110 => 'DataCompressionMethod',
|
||||
120 => 'QuantizationMethod',
|
||||
125 => 'EndPoints',
|
||||
130 => 'ExcursionTolerance',
|
||||
135 => 'BitsPerComponent',
|
||||
140 => 'MaximumDensityRange',
|
||||
145 => 'GammaCompensatedValue',
|
||||
),
|
||||
7 => array( // IPTC PreObjectData Tags
|
||||
10 => 'SizeMode',
|
||||
20 => 'MaxSubfileSize',
|
||||
90 => 'ObjectSizeAnnounced',
|
||||
95 => 'MaximumObjectSize',
|
||||
),
|
||||
8 => array( // IPTC ObjectData Tags
|
||||
10 => 'SubFile',
|
||||
),
|
||||
9 => array( // IPTC PostObjectData Tags
|
||||
10 => 'ConfirmedObjectSize',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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;
|
||||
|
|
@ -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'];
|
||||
// shortcut
|
||||
$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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?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.graphic.svg.php //
|
||||
// module for analyzing SVG Image files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_svg extends getid3_handler
|
||||
{
|
||||
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
|
||||
$SVGheader = fread($this->getid3->fp, 4096);
|
||||
if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) {
|
||||
$info['svg']['xml']['raw'] = $matches;
|
||||
}
|
||||
if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) {
|
||||
$info['svg']['doctype']['raw'] = $matches;
|
||||
}
|
||||
if (preg_match('#\<svg([^\>]+)\>#i', $SVGheader, $matches)) {
|
||||
$info['svg']['svg']['raw'] = $matches;
|
||||
}
|
||||
if (isset($info['svg']['svg']['raw'])) {
|
||||
|
||||
$sections_to_fix = array('xml', 'doctype', 'svg');
|
||||
foreach ($sections_to_fix as $section_to_fix) {
|
||||
if (!isset($info['svg'][$section_to_fix])) {
|
||||
continue;
|
||||
}
|
||||
$section_data = array();
|
||||
while (preg_match('/ "([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
||||
$section_data[] = $matches[1];
|
||||
$info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]);
|
||||
}
|
||||
while (preg_match('/([^\s]+)="([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
||||
$section_data[] = $matches[0];
|
||||
$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,]+/', $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] : '');
|
||||
$info['svg'][$section_to_fix]['sections'][$key] = trim($value, '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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($info['svg']['svg']['sections']['width'])) {
|
||||
$info['svg']['width'] = intval($info['svg']['svg']['sections']['width']);
|
||||
}
|
||||
if (!empty($info['svg']['svg']['sections']['height'])) {
|
||||
$info['svg']['height'] = intval($info['svg']['svg']['sections']['height']);
|
||||
}
|
||||
if (!empty($info['svg']['svg']['sections']['version'])) {
|
||||
$info['svg']['version'] = $info['svg']['svg']['sections']['version'];
|
||||
}
|
||||
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)) {
|
||||
$info['svg']['version'] = $matches[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($info['svg']['width'])) {
|
||||
$info['video']['resolution_x'] = $info['svg']['width'];
|
||||
}
|
||||
if (!empty($info['svg']['height'])) {
|
||||
$info['video']['resolution_y'] = $info['svg']['height'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
$info['error'][] = 'Did not find expected <svg> tag';
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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
|
||||
|
@ -163,7 +164,12 @@ class getid3_tiff
|
|||
case 306: // DateTime
|
||||
case 315: // Artist
|
||||
case 316: // HostComputer
|
||||
@$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
|
||||
$TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']);
|
||||
if (isset($info['tiff']['comments'][$TIFFcommentName])) {
|
||||
$info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
|
||||
} else {
|
||||
$info['tiff']['comments'][$TIFFcommentName] = array($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
|
@ -0,0 +1,312 @@
|
|||
<?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.misc.cue.php //
|
||||
// module for analyzing CUEsheet files //
|
||||
// dependencies: NONE //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Module originally written [2009-Mar-25] by //
|
||||
// Nigel Barnes <ngbarnesØhotmail*com> //
|
||||
// Minor reformatting and similar small changes to integrate //
|
||||
// into getID3 by James Heinrich <info@getid3.org> //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* CueSheet parser by Nigel Barnes.
|
||||
*
|
||||
* This is a PHP conversion of CueSharp 0.5 by Wyatt O'Day (wyday.com/cuesharp)
|
||||
*/
|
||||
|
||||
/**
|
||||
* A CueSheet class used to open and parse cuesheets.
|
||||
*
|
||||
*/
|
||||
class getid3_cue extends getid3_handler
|
||||
{
|
||||
var $cuesheet = array();
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$info['fileformat'] = 'cue';
|
||||
$this->readCueSheetFilename($info['filenamepath']);
|
||||
$info['cue'] = $this->cuesheet;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function readCueSheetFilename($filename)
|
||||
{
|
||||
$filedata = file_get_contents($filename);
|
||||
return $this->readCueSheet($filedata);
|
||||
}
|
||||
/**
|
||||
* Parses a cue sheet file.
|
||||
*
|
||||
* @param string $filename - The filename for the cue sheet to open.
|
||||
*/
|
||||
function readCueSheet(&$filedata)
|
||||
{
|
||||
$cue_lines = array();
|
||||
foreach (explode("\n", str_replace("\r", null, $filedata)) as $line)
|
||||
{
|
||||
if ( (strlen($line) > 0) && ($line[0] != '#'))
|
||||
{
|
||||
$cue_lines[] = trim($line);
|
||||
}
|
||||
}
|
||||
$this->parseCueSheet($cue_lines);
|
||||
|
||||
return $this->cuesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the cue sheet array.
|
||||
*
|
||||
* @param array $file - The cuesheet as an array of each line.
|
||||
*/
|
||||
function parseCueSheet($file)
|
||||
{
|
||||
//-1 means still global, all others are track specific
|
||||
$track_on = -1;
|
||||
|
||||
for ($i=0; $i < count($file); $i++)
|
||||
{
|
||||
list($key) = explode(' ', strtolower($file[$i]), 2);
|
||||
switch ($key)
|
||||
{
|
||||
case 'catalog':
|
||||
case 'cdtextfile':
|
||||
case 'isrc':
|
||||
case 'performer':
|
||||
case 'songwriter':
|
||||
case 'title':
|
||||
$this->parseString($file[$i], $track_on);
|
||||
break;
|
||||
case 'file':
|
||||
$currentFile = $this->parseFile($file[$i]);
|
||||
break;
|
||||
case 'flags':
|
||||
$this->parseFlags($file[$i], $track_on);
|
||||
break;
|
||||
case 'index':
|
||||
case 'postgap':
|
||||
case 'pregap':
|
||||
$this->parseIndex($file[$i], $track_on);
|
||||
break;
|
||||
case 'rem':
|
||||
$this->parseComment($file[$i], $track_on);
|
||||
break;
|
||||
case 'track':
|
||||
$track_on++;
|
||||
$this->parseTrack($file[$i], $track_on);
|
||||
if (isset($currentFile)) // if there's a file
|
||||
{
|
||||
$this->cuesheet['tracks'][$track_on]['datafile'] = $currentFile;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//save discarded junk and place string[] with track it was found in
|
||||
$this->parseGarbage($file[$i], $track_on);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the REM command.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the TRACK command.
|
||||
* @param integer $track_on - The track currently processing.
|
||||
*/
|
||||
function parseComment($line, $track_on)
|
||||
{
|
||||
$explodedline = explode(' ', $line, 3);
|
||||
$comment_REM = (isset($explodedline[0]) ? $explodedline[0] : '');
|
||||
$comment_type = (isset($explodedline[1]) ? $explodedline[1] : '');
|
||||
$comment_data = (isset($explodedline[2]) ? $explodedline[2] : '');
|
||||
if (($comment_REM == 'REM') && $comment_type) {
|
||||
$comment_type = strtolower($comment_type);
|
||||
$commment_data = trim($comment_data, ' "');
|
||||
if ($track_on != -1) {
|
||||
$this->cuesheet['tracks'][$track_on]['comments'][$comment_type][] = $comment_data;
|
||||
} else {
|
||||
$this->cuesheet['comments'][$comment_type][] = $comment_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the FILE command.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the FILE command.
|
||||
* @return array - Array of FILENAME and TYPE of file..
|
||||
*/
|
||||
function parseFile($line)
|
||||
{
|
||||
$line = substr($line, strpos($line, ' ') + 1);
|
||||
$type = strtolower(substr($line, strrpos($line, ' ')));
|
||||
|
||||
//remove type
|
||||
$line = substr($line, 0, strrpos($line, ' ') - 1);
|
||||
|
||||
//if quotes around it, remove them.
|
||||
$line = trim($line, '"');
|
||||
|
||||
return array('filename'=>$line, 'type'=>$type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the FLAG command.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the TRACK command.
|
||||
* @param integer $track_on - The track currently processing.
|
||||
*/
|
||||
function parseFlags($line, $track_on)
|
||||
{
|
||||
if ($track_on != -1)
|
||||
{
|
||||
foreach (explode(' ', strtolower($line)) as $type)
|
||||
{
|
||||
switch ($type)
|
||||
{
|
||||
case 'flags':
|
||||
// first entry in this line
|
||||
$this->cuesheet['tracks'][$track_on]['flags'] = array(
|
||||
'4ch' => false,
|
||||
'data' => false,
|
||||
'dcp' => false,
|
||||
'pre' => false,
|
||||
'scms' => false,
|
||||
);
|
||||
break;
|
||||
case 'data':
|
||||
case 'dcp':
|
||||
case '4ch':
|
||||
case 'pre':
|
||||
case 'scms':
|
||||
$this->cuesheet['tracks'][$track_on]['flags'][$type] = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect any unidentified data.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the TRACK command.
|
||||
* @param integer $track_on - The track currently processing.
|
||||
*/
|
||||
function parseGarbage($line, $track_on)
|
||||
{
|
||||
if ( strlen($line) > 0 )
|
||||
{
|
||||
if ($track_on == -1)
|
||||
{
|
||||
$this->cuesheet['garbage'][] = $line;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->cuesheet['tracks'][$track_on]['garbage'][] = $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the INDEX command of a TRACK.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the TRACK command.
|
||||
* @param integer $track_on - The track currently processing.
|
||||
*/
|
||||
function parseIndex($line, $track_on)
|
||||
{
|
||||
$type = strtolower(substr($line, 0, strpos($line, ' ')));
|
||||
$line = substr($line, strpos($line, ' ') + 1);
|
||||
|
||||
if ($type == 'index')
|
||||
{
|
||||
//read the index number
|
||||
$number = intval(substr($line, 0, strpos($line, ' ')));
|
||||
$line = substr($line, strpos($line, ' ') + 1);
|
||||
}
|
||||
|
||||
//extract the minutes, seconds, and frames
|
||||
$explodedline = explode(':', $line);
|
||||
$minutes = (isset($explodedline[0]) ? $explodedline[0] : '');
|
||||
$seconds = (isset($explodedline[1]) ? $explodedline[1] : '');
|
||||
$frames = (isset($explodedline[2]) ? $explodedline[2] : '');
|
||||
|
||||
switch ($type) {
|
||||
case 'index':
|
||||
$this->cuesheet['tracks'][$track_on][$type][$number] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
|
||||
break;
|
||||
case 'pregap':
|
||||
case 'postgap':
|
||||
$this->cuesheet['tracks'][$track_on][$type] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function parseString($line, $track_on)
|
||||
{
|
||||
$category = strtolower(substr($line, 0, strpos($line, ' ')));
|
||||
$line = substr($line, strpos($line, ' ') + 1);
|
||||
|
||||
//get rid of the quotes
|
||||
$line = trim($line, '"');
|
||||
|
||||
switch ($category)
|
||||
{
|
||||
case 'catalog':
|
||||
case 'cdtextfile':
|
||||
case 'isrc':
|
||||
case 'performer':
|
||||
case 'songwriter':
|
||||
case 'title':
|
||||
if ($track_on == -1)
|
||||
{
|
||||
$this->cuesheet[$category] = $line;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->cuesheet['tracks'][$track_on][$category] = $line;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the TRACK command.
|
||||
*
|
||||
* @param string $line - The line in the cue file that contains the TRACK command.
|
||||
* @param integer $track_on - The track currently processing.
|
||||
*/
|
||||
function parseTrack($line, $track_on)
|
||||
{
|
||||
$line = substr($line, strpos($line, ' ') + 1);
|
||||
$track = ltrim(substr($line, 0, strpos($line, ' ')), '0');
|
||||
|
||||
//find the data type.
|
||||
$datatype = strtolower(substr($line, strpos($line, ' ') + 1));
|
||||
|
||||
$this->cuesheet['tracks'][$track_on] = array('track_number'=>$track, 'datatype'=>$datatype);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,61 @@
|
|||
<?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.misc.exe.php //
|
||||
// module for analyzing EXE files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_exe extends getid3_handler
|
||||
{
|
||||
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
|
||||
$EXEheader = fread($this->getid3->fp, 28);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$info['fileformat'] = 'exe';
|
||||
$info['exe']['mz']['magic'] = 'MZ';
|
||||
|
||||
$info['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
|
||||
$info['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
|
||||
$info['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
|
||||
$info['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
|
||||
$info['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
|
||||
$info['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
|
||||
$info['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
|
||||
$info['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
|
||||
$info['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
|
||||
$info['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
|
||||
$info['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
|
||||
$info['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
|
||||
|
||||
$info['exe']['mz']['byte_size'] = (($info['exe']['mz']['raw']['page_count'] - 1)) * 512 + $info['exe']['mz']['raw']['last_page_size'];
|
||||
$info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16;
|
||||
$info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
|
||||
$info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
|
||||
|
||||
$info['error'][] = 'EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -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;
|
|
@ -14,15 +14,23 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_doc
|
||||
class getid3_msoffice extends getid3_handler
|
||||
{
|
||||
|
||||
function getid3_doc(&$fd, &$ThisFileInfo) {
|
||||
function Analyze() {
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'doc';
|
||||
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) {
|
||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.';
|
||||
return false;
|
||||
}
|
||||
$info['fileformat'] = 'msoffice';
|
||||
|
||||
$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
|
||||
return false;
|
||||
$info['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
||||
return false;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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 ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than 2GB';
|
||||
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;
|
||||
|
@ -125,6 +128,8 @@ class getid3_apetag
|
|||
$thisfile_ape['items'][$item_key] = array();
|
||||
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
|
||||
|
||||
$thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
|
||||
|
||||
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
|
||||
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
|
||||
$offset += $value_size;
|
||||
|
@ -150,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;
|
||||
|
||||
|
@ -163,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;
|
||||
|
||||
|
@ -187,23 +192,100 @@ class getid3_apetag
|
|||
break;
|
||||
|
||||
case 'tracknumber':
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments']['track'][] = $comment;
|
||||
if (is_array($thisfile_ape_items_current['data'])) {
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments']['track'][] = $comment;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cover art (artist)':
|
||||
case 'cover art (back)':
|
||||
case 'cover art (band logo)':
|
||||
case 'cover art (band)':
|
||||
case 'cover art (colored fish)':
|
||||
case 'cover art (composer)':
|
||||
case 'cover art (conductor)':
|
||||
case 'cover art (front)':
|
||||
case 'cover art (icon)':
|
||||
case 'cover art (illustration)':
|
||||
case 'cover art (lead)':
|
||||
case 'cover art (leaflet)':
|
||||
case 'cover art (lyricist)':
|
||||
case 'cover art (media)':
|
||||
case 'cover art (movie scene)':
|
||||
case 'cover art (other icon)':
|
||||
case 'cover art (other)':
|
||||
case 'cover art (performance)':
|
||||
case 'cover art (publisher logo)':
|
||||
case 'cover art (recording)':
|
||||
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['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]);
|
||||
|
||||
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:
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
||||
if (is_array($thisfile_ape_items_current['data'])) {
|
||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (empty($thisfile_replaygain)) {
|
||||
unset($ThisFileInfo['replay_gain']);
|
||||
unset($info['replay_gain']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -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 ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
|
||||
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));
|
||||
|
@ -51,7 +52,7 @@ class getid3_id3v1
|
|||
if (!empty($ParsedID3v1['genre'])) {
|
||||
unset($ParsedID3v1['genreid']);
|
||||
}
|
||||
if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
|
||||
if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) {
|
||||
unset($ParsedID3v1['genre']);
|
||||
}
|
||||
|
||||
|
@ -67,17 +68,17 @@ class getid3_id3v1
|
|||
$ParsedID3v1['year'],
|
||||
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
|
||||
$ParsedID3v1['comment'],
|
||||
@$ParsedID3v1['track']);
|
||||
(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
|
||||
$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,19 +94,19 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function cutfield($str) {
|
||||
static function cutfield($str) {
|
||||
return trim(substr($str, 0, strcspn($str, "\x00")));
|
||||
}
|
||||
|
||||
function ArrayOfGenres($allowSCMPXextended=false) {
|
||||
static function ArrayOfGenres($allowSCMPXextended=false) {
|
||||
static $GenreLookup = array(
|
||||
0 => 'Blues',
|
||||
1 => 'Classic Rock',
|
||||
|
@ -251,7 +252,7 @@ class getid3_id3v1
|
|||
141 => 'Christian Rock',
|
||||
142 => 'Merengue',
|
||||
143 => 'Salsa',
|
||||
144 => 'Trash Metal',
|
||||
144 => 'Thrash Metal',
|
||||
145 => 'Anime',
|
||||
146 => 'JPop',
|
||||
147 => 'Synthpop',
|
||||
|
@ -289,12 +290,15 @@ class getid3_id3v1
|
|||
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
|
||||
}
|
||||
|
||||
function LookupGenreName($genreid, $allowSCMPXextended=true) {
|
||||
static function LookupGenreName($genreid, $allowSCMPXextended=true) {
|
||||
switch ($genreid) {
|
||||
case 'RX':
|
||||
case 'CR':
|
||||
break;
|
||||
default:
|
||||
if (!is_numeric($genreid)) {
|
||||
return false;
|
||||
}
|
||||
$genreid = intval($genreid); // to handle 3 or '3' or '03'
|
||||
break;
|
||||
}
|
||||
|
@ -302,28 +306,25 @@ class getid3_id3v1
|
|||
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
||||
}
|
||||
|
||||
function LookupGenreID($genre, $allowSCMPXextended=false) {
|
||||
static function LookupGenreID($genre, $allowSCMPXextended=false) {
|
||||
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
|
||||
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
|
||||
foreach ($GenreLookup as $key => $value) {
|
||||
foreach ($GenreLookup as $key => $value) {
|
||||
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
||||
return $key;
|
||||
}
|
||||
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
||||
return $key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
||||
return false;
|
||||
}
|
||||
|
||||
function StandardiseID3v1GenreName($OriginalGenre) {
|
||||
static function StandardiseID3v1GenreName($OriginalGenre) {
|
||||
if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
|
||||
return getid3_id3v1::LookupGenreName($GenreID);
|
||||
}
|
||||
return $OriginalGenre;
|
||||
}
|
||||
|
||||
function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
|
||||
static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
|
||||
$ID3v1Tag = 'TAG';
|
||||
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
File diff suppressed because it is too large
Load Diff
|
@ -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 ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
|
||||
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
|
||||
|
||||
if ($endoffset >= pow(2, 31)) {
|
||||
$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
|
||||
$info = &$this->getid3->info;
|
||||
|
||||
if (!getid3_lib::intValueSupported($endoffset)) {
|
||||
$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;
|
||||
|
@ -192,9 +207,9 @@ class getid3_lyrics3
|
|||
foreach ($imagestrings as $key => $imagestring) {
|
||||
if (strpos($imagestring, '||') !== false) {
|
||||
$imagearray = explode('||', $imagestring);
|
||||
$ParsedLyrics3['images'][$key]['filename'] = @$imagearray[0];
|
||||
$ParsedLyrics3['images'][$key]['description'] = @$imagearray[1];
|
||||
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(@$imagearray[2]);
|
||||
$ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : '');
|
||||
$ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : '');
|
||||
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,37 +217,37 @@ 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;
|
||||
}
|
||||
|
||||
function Lyrics3Timestamp2Seconds($rawtimestamp) {
|
||||
if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) {
|
||||
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
|
||||
return (int) (($regs[1] * 60) + $regs[2]);
|
||||
}
|
||||
return false;
|
||||
|
@ -243,7 +258,7 @@ class getid3_lyrics3
|
|||
foreach ($lyricsarray as $key => $lyricline) {
|
||||
$regs = array();
|
||||
unset($thislinetimestamps);
|
||||
while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) {
|
||||
while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) {
|
||||
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
|
||||
$lyricline = str_replace($regs[0], '', $lyricline);
|
||||
}
|
|
@ -0,0 +1,766 @@
|
|||
<?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.tag.xmp.php //
|
||||
// module for analyzing XMP metadata (e.g. in JPEG files) //
|
||||
// dependencies: NONE //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Module originally written [2009-Mar-26] by //
|
||||
// Nigel Barnes <ngbarnesØhotmail*com> //
|
||||
// Bundled into getID3 with permission //
|
||||
// called by getID3 in module.graphic.jpg.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**************************************************************************************************
|
||||
* SWISScenter Source Nigel Barnes
|
||||
*
|
||||
* Provides functions for reading information from the 'APP1' Extensible Metadata
|
||||
* Platform (XMP) segment of JPEG format files.
|
||||
* This XMP segment is XML based and contains the Resource Description Framework (RDF)
|
||||
* data, which itself can contain the Dublin Core Metadata Initiative (DCMI) information.
|
||||
*
|
||||
* This code uses segments from the JPEG Metadata Toolkit project by Evan Hunter.
|
||||
*************************************************************************************************/
|
||||
class Image_XMP
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* The name of the image file that contains the XMP fields to extract and modify.
|
||||
* @see Image_XMP()
|
||||
*/
|
||||
var $_sFilename = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* The XMP fields that were extracted from the image or updated by this class.
|
||||
* @see getAllTags()
|
||||
*/
|
||||
var $_aXMP = array();
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* True if an APP1 segment was found to contain XMP metadata.
|
||||
* @see isValid()
|
||||
*/
|
||||
var $_bXMPParse = false;
|
||||
|
||||
/**
|
||||
* Returns the status of XMP parsing during instantiation
|
||||
*
|
||||
* You'll normally want to call this method before trying to get XMP fields.
|
||||
*
|
||||
* @return boolean
|
||||
* Returns true if an APP1 segment was found to contain XMP metadata.
|
||||
*/
|
||||
function isValid()
|
||||
{
|
||||
return $this->_bXMPParse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of all XMP tags extracted from the image
|
||||
*
|
||||
* @return array - An array of XMP fields as it extracted by the XMPparse() function
|
||||
*/
|
||||
function getAllTags()
|
||||
{
|
||||
return $this->_aXMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the JPEG header segments from an JPEG image file into an array
|
||||
*
|
||||
* @param string $filename - the filename of the JPEG file to read
|
||||
* @return array $headerdata - Array of JPEG header segments
|
||||
* @return boolean FALSE - if headers could not be read
|
||||
*/
|
||||
function _get_jpeg_header_data($filename)
|
||||
{
|
||||
// prevent refresh from aborting file operations and hosing file
|
||||
ignore_user_abort(true);
|
||||
|
||||
// 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.
|
||||
if (is_readable($filename) && is_file($filename) && ($filehnd = fopen($filename, 'rb'))) {
|
||||
// great
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the first two characters
|
||||
$data = fread($filehnd, 2);
|
||||
|
||||
// Check that the first two characters are 0xFF 0xD8 (SOI - Start of image)
|
||||
if ($data != "\xFF\xD8")
|
||||
{
|
||||
// No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
|
||||
echo '<p>This probably is not a JPEG file</p>'."\n";
|
||||
fclose($filehnd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the third character
|
||||
$data = fread($filehnd, 2);
|
||||
|
||||
// Check that the third character is 0xFF (Start of first segment header)
|
||||
if ($data{0} != "\xFF")
|
||||
{
|
||||
// NO FF found - close file and return - JPEG is probably corrupted
|
||||
fclose($filehnd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flag that we havent yet hit the compressed image data
|
||||
$hit_compressed_image_data = false;
|
||||
|
||||
// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
|
||||
// 2) we have hit the compressed image data (no more headers are allowed after data)
|
||||
// 3) or end of file is hit
|
||||
|
||||
while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
|
||||
{
|
||||
// Found a segment to look at.
|
||||
// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
|
||||
if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
|
||||
{
|
||||
// Segment isn't a Restart marker
|
||||
// Read the next two bytes (size)
|
||||
$sizestr = fread($filehnd, 2);
|
||||
|
||||
// convert the size bytes to an integer
|
||||
$decodedsize = unpack('nsize', $sizestr);
|
||||
|
||||
// Save the start position of the data
|
||||
$segdatastart = ftell($filehnd);
|
||||
|
||||
// Read the segment data with length indicated by the previously read size
|
||||
$segdata = fread($filehnd, $decodedsize['size'] - 2);
|
||||
|
||||
// Store the segment information in the output array
|
||||
$headerdata[] = array(
|
||||
'SegType' => ord($data{1}),
|
||||
'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
|
||||
'SegDataStart' => $segdatastart,
|
||||
'SegData' => $segdata,
|
||||
);
|
||||
}
|
||||
|
||||
// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
|
||||
if ($data{1} == "\xDA")
|
||||
{
|
||||
// Flag that we have hit the compressed image data - exit loop as no more headers available.
|
||||
$hit_compressed_image_data = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
|
||||
$data = fread($filehnd, 2);
|
||||
|
||||
// Check that the first byte of the two is 0xFF as it should be for a marker
|
||||
if ($data{0} != "\xFF")
|
||||
{
|
||||
// NO FF found - close file and return - JPEG is probably corrupted
|
||||
fclose($filehnd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close File
|
||||
fclose($filehnd);
|
||||
// Alow the user to abort from now on
|
||||
ignore_user_abort(false);
|
||||
|
||||
// Return the header data retrieved
|
||||
return $headerdata;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves XMP information from an APP1 JPEG segment and returns the raw XML text as a string.
|
||||
*
|
||||
* @param string $filename - the filename of the JPEG file to read
|
||||
* @return string $xmp_data - the string of raw XML text
|
||||
* @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured
|
||||
*/
|
||||
function _get_XMP_text($filename)
|
||||
{
|
||||
//Get JPEG header data
|
||||
$jpeg_header_data = $this->_get_jpeg_header_data($filename);
|
||||
|
||||
//Cycle through the header segments
|
||||
for ($i = 0; $i < count($jpeg_header_data); $i++)
|
||||
{
|
||||
// If we find an APP1 header,
|
||||
if (strcmp($jpeg_header_data[$i]['SegName'], 'APP1') == 0)
|
||||
{
|
||||
// And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
|
||||
if (strncmp($jpeg_header_data[$i]['SegData'], 'http://ns.adobe.com/xap/1.0/'."\x00", 29) == 0)
|
||||
{
|
||||
// Found a XMP/RDF block
|
||||
// Return the XMP text
|
||||
$xmp_data = substr($jpeg_header_data[$i]['SegData'], 29);
|
||||
|
||||
return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string containing XMP data (XML), and returns an array
|
||||
* which contains all the XMP (XML) information.
|
||||
*
|
||||
* @param string $xml_text - a string containing the XMP data (XML) to be parsed
|
||||
* @return array $xmp_array - an array containing all xmp details retrieved.
|
||||
* @return boolean FALSE - couldn't parse the XMP data
|
||||
*/
|
||||
function read_XMP_array_from_text($xmltext)
|
||||
{
|
||||
// Check if there actually is any text to parse
|
||||
if (trim($xmltext) == '')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an instance of a xml parser to parse the XML text
|
||||
$xml_parser = xml_parser_create('UTF-8');
|
||||
|
||||
// Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10
|
||||
|
||||
// We would like to remove unneccessary white space, but this will also
|
||||
// remove things like newlines (
) in the XML values, so white space
|
||||
// will have to be removed later
|
||||
if (xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 0) == false)
|
||||
{
|
||||
// Error setting case folding - destroy the parser and return
|
||||
xml_parser_free($xml_parser);
|
||||
return false;
|
||||
}
|
||||
|
||||
// to use XML code correctly we have to turn case folding
|
||||
// (uppercasing) off. XML is case sensitive and upper
|
||||
// casing is in reality XML standards violation
|
||||
if (xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0) == false)
|
||||
{
|
||||
// Error setting case folding - destroy the parser and return
|
||||
xml_parser_free($xml_parser);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the XML text into a array structure
|
||||
if (xml_parse_into_struct($xml_parser, $xmltext, $values, $tags) == 0)
|
||||
{
|
||||
// Error Parsing XML - destroy the parser and return
|
||||
xml_parser_free($xml_parser);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destroy the xml parser
|
||||
xml_parser_free($xml_parser);
|
||||
|
||||
// Clear the output array
|
||||
$xmp_array = array();
|
||||
|
||||
// The XMP data has now been parsed into an array ...
|
||||
|
||||
// Cycle through each of the array elements
|
||||
$current_property = ''; // current property being processed
|
||||
$container_index = -1; // -1 = no container open, otherwise index of container content
|
||||
foreach ($values as $xml_elem)
|
||||
{
|
||||
// Syntax and Class names
|
||||
switch ($xml_elem['tag'])
|
||||
{
|
||||
case 'x:xmpmeta':
|
||||
// only defined attribute is x:xmptk written by Adobe XMP Toolkit; value is the version of the toolkit
|
||||
break;
|
||||
|
||||
case 'rdf:RDF':
|
||||
// required element immediately within x:xmpmeta; no data here
|
||||
break;
|
||||
|
||||
case 'rdf:Description':
|
||||
switch ($xml_elem['type'])
|
||||
{
|
||||
case 'open':
|
||||
case 'complete':
|
||||
if (array_key_exists('attributes', $xml_elem))
|
||||
{
|
||||
// rdf:Description may contain wanted attributes
|
||||
foreach (array_keys($xml_elem['attributes']) as $key)
|
||||
{
|
||||
// Check whether we want this details from this attribute
|
||||
if (in_array($key, $GLOBALS['XMP_tag_captions']))
|
||||
{
|
||||
// Attribute wanted
|
||||
$xmp_array[$key] = $xml_elem['attributes'][$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
case 'cdata':
|
||||
case 'close':
|
||||
break;
|
||||
}
|
||||
|
||||
case 'rdf:ID':
|
||||
case 'rdf:nodeID':
|
||||
// Attributes are ignored
|
||||
break;
|
||||
|
||||
case 'rdf:li':
|
||||
// Property member
|
||||
if ($xml_elem['type'] == 'complete')
|
||||
{
|
||||
if (array_key_exists('attributes', $xml_elem))
|
||||
{
|
||||
// If Lang Alt (language alternatives) then ensure we take the default language
|
||||
if (isset($xml_elem['attributes']['xml:lang']) && ($xml_elem['attributes']['xml:lang'] != 'x-default'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($current_property != '')
|
||||
{
|
||||
$xmp_array[$current_property][$container_index] = (isset($xml_elem['value']) ? $xml_elem['value'] : '');
|
||||
$container_index += 1;
|
||||
}
|
||||
//else unidentified attribute!!
|
||||
}
|
||||
break;
|
||||
|
||||
case 'rdf:Seq':
|
||||
case 'rdf:Bag':
|
||||
case 'rdf:Alt':
|
||||
// Container found
|
||||
switch ($xml_elem['type'])
|
||||
{
|
||||
case 'open':
|
||||
$container_index = 0;
|
||||
break;
|
||||
case 'close':
|
||||
$container_index = -1;
|
||||
break;
|
||||
case 'cdata':
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Check whether we want the details from this attribute
|
||||
if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
|
||||
{
|
||||
switch ($xml_elem['type'])
|
||||
{
|
||||
case 'open':
|
||||
// open current element
|
||||
$current_property = $xml_elem['tag'];
|
||||
break;
|
||||
|
||||
case 'close':
|
||||
// close current element
|
||||
$current_property = '';
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
// store attribute value
|
||||
$xmp_array[$xml_elem['tag']] = (isset($xml_elem['value']) ? $xml_elem['value'] : '');
|
||||
break;
|
||||
|
||||
case 'cdata':
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return $xmp_array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string - Name of the image file to access and extract XMP information from.
|
||||
*/
|
||||
function Image_XMP($sFilename)
|
||||
{
|
||||
$this->_sFilename = $sFilename;
|
||||
|
||||
if (is_file($this->_sFilename))
|
||||
{
|
||||
// Get XMP data
|
||||
$xmp_data = $this->_get_XMP_text($sFilename);
|
||||
if ($xmp_data)
|
||||
{
|
||||
$this->_aXMP = $this->read_XMP_array_from_text($xmp_data);
|
||||
$this->_bXMPParse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Global Variable: XMP_tag_captions
|
||||
*
|
||||
* The Property names of all known XMP fields.
|
||||
* Note: this is a full list with unrequired properties commented out.
|
||||
*/
|
||||
$GLOBALS['XMP_tag_captions'] = array(
|
||||
// IPTC Core
|
||||
'Iptc4xmpCore:CiAdrCity',
|
||||
'Iptc4xmpCore:CiAdrCtry',
|
||||
'Iptc4xmpCore:CiAdrExtadr',
|
||||
'Iptc4xmpCore:CiAdrPcode',
|
||||
'Iptc4xmpCore:CiAdrRegion',
|
||||
'Iptc4xmpCore:CiEmailWork',
|
||||
'Iptc4xmpCore:CiTelWork',
|
||||
'Iptc4xmpCore:CiUrlWork',
|
||||
'Iptc4xmpCore:CountryCode',
|
||||
'Iptc4xmpCore:CreatorContactInfo',
|
||||
'Iptc4xmpCore:IntellectualGenre',
|
||||
'Iptc4xmpCore:Location',
|
||||
'Iptc4xmpCore:Scene',
|
||||
'Iptc4xmpCore:SubjectCode',
|
||||
// Dublin Core Schema
|
||||
'dc:contributor',
|
||||
'dc:coverage',
|
||||
'dc:creator',
|
||||
'dc:date',
|
||||
'dc:description',
|
||||
'dc:format',
|
||||
'dc:identifier',
|
||||
'dc:language',
|
||||
'dc:publisher',
|
||||
'dc:relation',
|
||||
'dc:rights',
|
||||
'dc:source',
|
||||
'dc:subject',
|
||||
'dc:title',
|
||||
'dc:type',
|
||||
// XMP Basic Schema
|
||||
'xmp:Advisory',
|
||||
'xmp:BaseURL',
|
||||
'xmp:CreateDate',
|
||||
'xmp:CreatorTool',
|
||||
'xmp:Identifier',
|
||||
'xmp:Label',
|
||||
'xmp:MetadataDate',
|
||||
'xmp:ModifyDate',
|
||||
'xmp:Nickname',
|
||||
'xmp:Rating',
|
||||
'xmp:Thumbnails',
|
||||
'xmpidq:Scheme',
|
||||
// XMP Rights Management Schema
|
||||
'xmpRights:Certificate',
|
||||
'xmpRights:Marked',
|
||||
'xmpRights:Owner',
|
||||
'xmpRights:UsageTerms',
|
||||
'xmpRights:WebStatement',
|
||||
// These are not in spec but Photoshop CS seems to use them
|
||||
'xap:Advisory',
|
||||
'xap:BaseURL',
|
||||
'xap:CreateDate',
|
||||
'xap:CreatorTool',
|
||||
'xap:Identifier',
|
||||
'xap:MetadataDate',
|
||||
'xap:ModifyDate',
|
||||
'xap:Nickname',
|
||||
'xap:Rating',
|
||||
'xap:Thumbnails',
|
||||
'xapidq:Scheme',
|
||||
'xapRights:Certificate',
|
||||
'xapRights:Copyright',
|
||||
'xapRights:Marked',
|
||||
'xapRights:Owner',
|
||||
'xapRights:UsageTerms',
|
||||
'xapRights:WebStatement',
|
||||
// XMP Media Management Schema
|
||||
'xapMM:DerivedFrom',
|
||||
'xapMM:DocumentID',
|
||||
'xapMM:History',
|
||||
'xapMM:InstanceID',
|
||||
'xapMM:ManagedFrom',
|
||||
'xapMM:Manager',
|
||||
'xapMM:ManageTo',
|
||||
'xapMM:ManageUI',
|
||||
'xapMM:ManagerVariant',
|
||||
'xapMM:RenditionClass',
|
||||
'xapMM:RenditionParams',
|
||||
'xapMM:VersionID',
|
||||
'xapMM:Versions',
|
||||
'xapMM:LastURL',
|
||||
'xapMM:RenditionOf',
|
||||
'xapMM:SaveID',
|
||||
// XMP Basic Job Ticket Schema
|
||||
'xapBJ:JobRef',
|
||||
// XMP Paged-Text Schema
|
||||
'xmpTPg:MaxPageSize',
|
||||
'xmpTPg:NPages',
|
||||
'xmpTPg:Fonts',
|
||||
'xmpTPg:Colorants',
|
||||
'xmpTPg:PlateNames',
|
||||
// Adobe PDF Schema
|
||||
'pdf:Keywords',
|
||||
'pdf:PDFVersion',
|
||||
'pdf:Producer',
|
||||
// Photoshop Schema
|
||||
'photoshop:AuthorsPosition',
|
||||
'photoshop:CaptionWriter',
|
||||
'photoshop:Category',
|
||||
'photoshop:City',
|
||||
'photoshop:Country',
|
||||
'photoshop:Credit',
|
||||
'photoshop:DateCreated',
|
||||
'photoshop:Headline',
|
||||
'photoshop:History',
|
||||
// Not in XMP spec
|
||||
'photoshop:Instructions',
|
||||
'photoshop:Source',
|
||||
'photoshop:State',
|
||||
'photoshop:SupplementalCategories',
|
||||
'photoshop:TransmissionReference',
|
||||
'photoshop:Urgency',
|
||||
// EXIF Schemas
|
||||
'tiff:ImageWidth',
|
||||
'tiff:ImageLength',
|
||||
'tiff:BitsPerSample',
|
||||
'tiff:Compression',
|
||||
'tiff:PhotometricInterpretation',
|
||||
'tiff:Orientation',
|
||||
'tiff:SamplesPerPixel',
|
||||
'tiff:PlanarConfiguration',
|
||||
'tiff:YCbCrSubSampling',
|
||||
'tiff:YCbCrPositioning',
|
||||
'tiff:XResolution',
|
||||
'tiff:YResolution',
|
||||
'tiff:ResolutionUnit',
|
||||
'tiff:TransferFunction',
|
||||
'tiff:WhitePoint',
|
||||
'tiff:PrimaryChromaticities',
|
||||
'tiff:YCbCrCoefficients',
|
||||
'tiff:ReferenceBlackWhite',
|
||||
'tiff:DateTime',
|
||||
'tiff:ImageDescription',
|
||||
'tiff:Make',
|
||||
'tiff:Model',
|
||||
'tiff:Software',
|
||||
'tiff:Artist',
|
||||
'tiff:Copyright',
|
||||
'exif:ExifVersion',
|
||||
'exif:FlashpixVersion',
|
||||
'exif:ColorSpace',
|
||||
'exif:ComponentsConfiguration',
|
||||
'exif:CompressedBitsPerPixel',
|
||||
'exif:PixelXDimension',
|
||||
'exif:PixelYDimension',
|
||||
'exif:MakerNote',
|
||||
'exif:UserComment',
|
||||
'exif:RelatedSoundFile',
|
||||
'exif:DateTimeOriginal',
|
||||
'exif:DateTimeDigitized',
|
||||
'exif:ExposureTime',
|
||||
'exif:FNumber',
|
||||
'exif:ExposureProgram',
|
||||
'exif:SpectralSensitivity',
|
||||
'exif:ISOSpeedRatings',
|
||||
'exif:OECF',
|
||||
'exif:ShutterSpeedValue',
|
||||
'exif:ApertureValue',
|
||||
'exif:BrightnessValue',
|
||||
'exif:ExposureBiasValue',
|
||||
'exif:MaxApertureValue',
|
||||
'exif:SubjectDistance',
|
||||
'exif:MeteringMode',
|
||||
'exif:LightSource',
|
||||
'exif:Flash',
|
||||
'exif:FocalLength',
|
||||
'exif:SubjectArea',
|
||||
'exif:FlashEnergy',
|
||||
'exif:SpatialFrequencyResponse',
|
||||
'exif:FocalPlaneXResolution',
|
||||
'exif:FocalPlaneYResolution',
|
||||
'exif:FocalPlaneResolutionUnit',
|
||||
'exif:SubjectLocation',
|
||||
'exif:SensingMethod',
|
||||
'exif:FileSource',
|
||||
'exif:SceneType',
|
||||
'exif:CFAPattern',
|
||||
'exif:CustomRendered',
|
||||
'exif:ExposureMode',
|
||||
'exif:WhiteBalance',
|
||||
'exif:DigitalZoomRatio',
|
||||
'exif:FocalLengthIn35mmFilm',
|
||||
'exif:SceneCaptureType',
|
||||
'exif:GainControl',
|
||||
'exif:Contrast',
|
||||
'exif:Saturation',
|
||||
'exif:Sharpness',
|
||||
'exif:DeviceSettingDescription',
|
||||
'exif:SubjectDistanceRange',
|
||||
'exif:ImageUniqueID',
|
||||
'exif:GPSVersionID',
|
||||
'exif:GPSLatitude',
|
||||
'exif:GPSLongitude',
|
||||
'exif:GPSAltitudeRef',
|
||||
'exif:GPSAltitude',
|
||||
'exif:GPSTimeStamp',
|
||||
'exif:GPSSatellites',
|
||||
'exif:GPSStatus',
|
||||
'exif:GPSMeasureMode',
|
||||
'exif:GPSDOP',
|
||||
'exif:GPSSpeedRef',
|
||||
'exif:GPSSpeed',
|
||||
'exif:GPSTrackRef',
|
||||
'exif:GPSTrack',
|
||||
'exif:GPSImgDirectionRef',
|
||||
'exif:GPSImgDirection',
|
||||
'exif:GPSMapDatum',
|
||||
'exif:GPSDestLatitude',
|
||||
'exif:GPSDestLongitude',
|
||||
'exif:GPSDestBearingRef',
|
||||
'exif:GPSDestBearing',
|
||||
'exif:GPSDestDistanceRef',
|
||||
'exif:GPSDestDistance',
|
||||
'exif:GPSProcessingMethod',
|
||||
'exif:GPSAreaInformation',
|
||||
'exif:GPSDifferential',
|
||||
'stDim:w',
|
||||
'stDim:h',
|
||||
'stDim:unit',
|
||||
'xapGImg:height',
|
||||
'xapGImg:width',
|
||||
'xapGImg:format',
|
||||
'xapGImg:image',
|
||||
'stEvt:action',
|
||||
'stEvt:instanceID',
|
||||
'stEvt:parameters',
|
||||
'stEvt:softwareAgent',
|
||||
'stEvt:when',
|
||||
'stRef:instanceID',
|
||||
'stRef:documentID',
|
||||
'stRef:versionID',
|
||||
'stRef:renditionClass',
|
||||
'stRef:renditionParams',
|
||||
'stRef:manager',
|
||||
'stRef:managerVariant',
|
||||
'stRef:manageTo',
|
||||
'stRef:manageUI',
|
||||
'stVer:comments',
|
||||
'stVer:event',
|
||||
'stVer:modifyDate',
|
||||
'stVer:modifier',
|
||||
'stVer:version',
|
||||
'stJob:name',
|
||||
'stJob:id',
|
||||
'stJob:url',
|
||||
// Exif Flash
|
||||
'exif:Fired',
|
||||
'exif:Return',
|
||||
'exif:Mode',
|
||||
'exif:Function',
|
||||
'exif:RedEyeMode',
|
||||
// Exif OECF/SFR
|
||||
'exif:Columns',
|
||||
'exif:Rows',
|
||||
'exif:Names',
|
||||
'exif:Values',
|
||||
// Exif CFAPattern
|
||||
'exif:Columns',
|
||||
'exif:Rows',
|
||||
'exif:Values',
|
||||
// Exif DeviceSettings
|
||||
'exif:Columns',
|
||||
'exif:Rows',
|
||||
'exif:Settings',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Global Variable: JPEG_Segment_Names
|
||||
*
|
||||
* The names of the JPEG segment markers, indexed by their marker number
|
||||
*/
|
||||
$GLOBALS['JPEG_Segment_Names'] = array(
|
||||
0x01 => 'TEM',
|
||||
0x02 => 'RES',
|
||||
0xC0 => 'SOF0',
|
||||
0xC1 => 'SOF1',
|
||||
0xC2 => 'SOF2',
|
||||
0xC3 => 'SOF4',
|
||||
0xC4 => 'DHT',
|
||||
0xC5 => 'SOF5',
|
||||
0xC6 => 'SOF6',
|
||||
0xC7 => 'SOF7',
|
||||
0xC8 => 'JPG',
|
||||
0xC9 => 'SOF9',
|
||||
0xCA => 'SOF10',
|
||||
0xCB => 'SOF11',
|
||||
0xCC => 'DAC',
|
||||
0xCD => 'SOF13',
|
||||
0xCE => 'SOF14',
|
||||
0xCF => 'SOF15',
|
||||
0xD0 => 'RST0',
|
||||
0xD1 => 'RST1',
|
||||
0xD2 => 'RST2',
|
||||
0xD3 => 'RST3',
|
||||
0xD4 => 'RST4',
|
||||
0xD5 => 'RST5',
|
||||
0xD6 => 'RST6',
|
||||
0xD7 => 'RST7',
|
||||
0xD8 => 'SOI',
|
||||
0xD9 => 'EOI',
|
||||
0xDA => 'SOS',
|
||||
0xDB => 'DQT',
|
||||
0xDC => 'DNL',
|
||||
0xDD => 'DRI',
|
||||
0xDE => 'DHP',
|
||||
0xDF => 'EXP',
|
||||
0xE0 => 'APP0',
|
||||
0xE1 => 'APP1',
|
||||
0xE2 => 'APP2',
|
||||
0xE3 => 'APP3',
|
||||
0xE4 => 'APP4',
|
||||
0xE5 => 'APP5',
|
||||
0xE6 => 'APP6',
|
||||
0xE7 => 'APP7',
|
||||
0xE8 => 'APP8',
|
||||
0xE9 => 'APP9',
|
||||
0xEA => 'APP10',
|
||||
0xEB => 'APP11',
|
||||
0xEC => 'APP12',
|
||||
0xED => 'APP13',
|
||||
0xEE => 'APP14',
|
||||
0xEF => 'APP15',
|
||||
0xF0 => 'JPG0',
|
||||
0xF1 => 'JPG1',
|
||||
0xF2 => 'JPG2',
|
||||
0xF3 => 'JPG3',
|
||||
0xF4 => 'JPG4',
|
||||
0xF5 => 'JPG5',
|
||||
0xF6 => 'JPG6',
|
||||
0xF7 => 'JPG7',
|
||||
0xF8 => 'JPG8',
|
||||
0xF9 => 'JPG9',
|
||||
0xFA => 'JPG10',
|
||||
0xFB => 'JPG11',
|
||||
0xFC => 'JPG12',
|
||||
0xFD => 'JPG13',
|
||||
0xFE => 'COM',
|
||||
);
|
||||
|
||||
?>
|
|
@ -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,7 +56,7 @@ class getid3_write_apetag
|
|||
}
|
||||
|
||||
if ($APEtag = $this->GenerateAPEtag()) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
flock($fp, LOCK_EX);
|
||||
|
||||
|
@ -86,9 +86,7 @@ class getid3_write_apetag
|
|||
fclose($fp);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -97,7 +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'])) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
||||
|
||||
flock($fp, LOCK_EX);
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
|
@ -120,7 +118,6 @@ class getid3_write_apetag
|
|||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -204,7 +201,7 @@ class getid3_write_apetag
|
|||
}
|
||||
|
||||
function CleanAPEtagItemKey($itemkey) {
|
||||
$itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
|
||||
$itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
|
||||
|
||||
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
|
||||
switch (strtoupper($itemkey)) {
|
|
@ -18,6 +18,7 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE_
|
|||
class getid3_write_id3v1
|
||||
{
|
||||
var $filename;
|
||||
var $filesize;
|
||||
var $tag_data;
|
||||
var $warnings = array(); // any non-critical errors will be stored here
|
||||
var $errors = array(); // any critical errors will be stored here
|
||||
|
@ -27,15 +28,14 @@ class getid3_write_id3v1
|
|||
}
|
||||
|
||||
function WriteID3v1() {
|
||||
if ((filesize($this->filename) >= (pow(2, 31) - 128)) || (filesize($this->filename) < 0)) {
|
||||
$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
|
||||
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;
|
||||
}
|
||||
if ($fp_source = fopen($this->filename, 'r+b')) {
|
||||
fseek($fp_source, -128, SEEK_END);
|
||||
if (fread($fp_source, 3) == 'TAG') {
|
||||
fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
|
||||
|
@ -45,19 +45,19 @@ class getid3_write_id3v1
|
|||
$this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
|
||||
|
||||
$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
|
||||
@$this->tag_data['title'],
|
||||
@$this->tag_data['artist'],
|
||||
@$this->tag_data['album'],
|
||||
@$this->tag_data['year'],
|
||||
@$this->tag_data['genreid'],
|
||||
@$this->tag_data['comment'],
|
||||
@$this->tag_data['track']);
|
||||
(isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''),
|
||||
(isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''),
|
||||
(isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''),
|
||||
(isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''),
|
||||
(isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
|
||||
(isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
|
||||
(isset($this->tag_data['track'] ) ? $this->tag_data['track'] : ''));
|
||||
fwrite($fp_source, $new_id3v1_tag_data, 128);
|
||||
fclose($fp_source);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
|
||||
$this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -71,11 +71,12 @@ class getid3_write_id3v1
|
|||
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$getID3->option_tag_id3v2 = false;
|
||||
$getID3->option_tag_apetag = false;
|
||||
$getID3->option_tags_html = false;
|
||||
$getID3->option_extra_info = false;
|
||||
$getID3->option_tag_id3v1 = true;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
if ($ThisFileInfo['filesize'] >= (pow(2, 31) - 128)) {
|
||||
// cannot write tags on files > 2GB
|
||||
return false;
|
||||
}
|
||||
if (isset($ThisFileInfo['tags']['id3v1'])) {
|
||||
foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
|
||||
$id3v1data[$key] = implode(',', $value);
|
||||
|
@ -87,18 +88,18 @@ class getid3_write_id3v1
|
|||
}
|
||||
|
||||
function RemoveID3v1() {
|
||||
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
|
||||
return false;
|
||||
}
|
||||
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename)) {
|
||||
if ($fp_source = @fopen($this->filename, 'r+b')) {
|
||||
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;
|
||||
}
|
||||
if ($fp_source = fopen($this->filename, 'r+b')) {
|
||||
|
||||
fseek($fp_source, -128, SEEK_END);
|
||||
if (fread($fp_source, 3) == 'TAG') {
|
||||
ftruncate($fp_source, filesize($this->filename) - 128);
|
||||
ftruncate($fp_source, $this->filesize - 128);
|
||||
} else {
|
||||
// no ID3v1 tag to begin with - do nothing
|
||||
}
|
||||
|
@ -106,7 +107,7 @@ class getid3_write_id3v1
|
|||
return true;
|
||||
|
||||
} else {
|
||||
$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';
|
||||
|
@ -114,6 +115,24 @@ class getid3_write_id3v1
|
|||
return false;
|
||||
}
|
||||
|
||||
function setRealFileSize() {
|
||||
if (PHP_INT_MAX > 2147483647) {
|
||||
$this->filesize = filesize($this->filename);
|
||||
return true;
|
||||
}
|
||||
// 32-bit PHP will not return correct values for filesize() if file is >=2GB
|
||||
// but getID3->analyze() has workarounds to get actual filesize
|
||||
$getID3 = new getID3;
|
||||
$getID3->option_tag_id3v1 = false;
|
||||
$getID3->option_tag_id3v2 = false;
|
||||
$getID3->option_tag_apetag = false;
|
||||
$getID3->option_tags_html = false;
|
||||
$getID3->option_extra_info = false;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
$this->filesize = $ThisFileInfo['filesize'];
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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
|
||||
|
@ -36,12 +37,12 @@ class getid3_write_id3v2
|
|||
// File MUST be writeable - CHMOD(646) at least. It's best if the
|
||||
// directory is also writeable, because that method is both faster and less susceptible to errors.
|
||||
|
||||
if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
|
||||
if (!empty($this->filename) && (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename))))) {
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$OldThisFileInfo = $getID3->analyze($this->filename);
|
||||
if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$this->errors[] = 'Unable to write ID3v2 because file is larger than 2GB';
|
||||
if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
|
||||
$this->errors[] = 'Unable to write ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
@ -51,7 +52,7 @@ class getid3_write_id3v2
|
|||
$this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
|
||||
}
|
||||
}
|
||||
$this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
|
||||
$this->paddedlength = (isset($OldThisFileInfo['id3v2']['headerlength']) ? max($OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength) : $this->paddedlength);
|
||||
|
||||
if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
|
||||
|
||||
|
@ -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')) {
|
||||
ob_start();
|
||||
if ($fp_source = fopen($this->filename, 'rb')) {
|
||||
if ($fp_temp = fopen($tempfilename, 'wb')) {
|
||||
if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
|
||||
if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
|
||||
if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
|
||||
|
||||
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;
|
||||
|
||||
|
@ -138,7 +128,7 @@ class getid3_write_id3v2
|
|||
}
|
||||
return true;
|
||||
} else {
|
||||
$this->errors[] = '!is_writeable('.$this->filename.')';
|
||||
$this->errors[] = 'WriteID3v2() failed: !is_writeable('.$this->filename.')';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -150,12 +140,13 @@ 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
|
||||
if ($fp_source = @fopen($this->filename, 'rb')) {
|
||||
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);
|
||||
if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$this->errors[] = 'Unable to remove ID3v2 because file is larger than 2GB';
|
||||
if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
|
||||
$this->errors[] = 'Unable to remove ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
@ -163,17 +154,17 @@ class getid3_write_id3v2
|
|||
if ($OldThisFileInfo['avdataoffset'] !== false) {
|
||||
fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
}
|
||||
if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
|
||||
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 {
|
||||
$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 {
|
||||
$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);
|
||||
|
@ -184,12 +175,13 @@ 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.
|
||||
if ($fp_source = @fopen($this->filename, 'rb')) {
|
||||
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);
|
||||
if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
|
||||
$this->errors[] = 'Unable to remove ID3v2 because file is larger than 2GB';
|
||||
if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
|
||||
$this->errors[] = 'Unable to remove ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
||||
fclose($fp_source);
|
||||
return false;
|
||||
}
|
||||
|
@ -198,26 +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);
|
||||
if ($fp_source = @fopen($this->filename, 'wb')) {
|
||||
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 {
|
||||
$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 {
|
||||
$this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
|
||||
$this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -237,25 +229,25 @@ class getid3_write_id3v2
|
|||
switch ($this->majorversion) {
|
||||
case 4:
|
||||
// %abcd0000
|
||||
$flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
|
||||
$flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
|
||||
$flag .= (@$flags['footer'] ? '1' : '0'); // d - Footer present
|
||||
$flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (!empty($flags['extendedheader'] ) ? '1' : '0'); // b - Extended header
|
||||
$flag .= (!empty($flags['experimental'] ) ? '1' : '0'); // c - Experimental indicator
|
||||
$flag .= (!empty($flags['footer'] ) ? '1' : '0'); // d - Footer present
|
||||
$flag .= '0000';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// %abc00000
|
||||
$flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
|
||||
$flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
|
||||
$flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (!empty($flags['extendedheader'] ) ? '1' : '0'); // b - Extended header
|
||||
$flag .= (!empty($flags['experimental'] ) ? '1' : '0'); // c - Experimental indicator
|
||||
$flag .= '00000';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// %ab000000
|
||||
$flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (@$flags['compression'] ? '1' : '0'); // b - Compression
|
||||
$flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
|
||||
$flag .= (!empty($flags['compression'] ) ? '1' : '0'); // b - Compression
|
||||
$flag .= '000000';
|
||||
break;
|
||||
|
||||
|
@ -777,7 +769,7 @@ class getid3_write_id3v2
|
|||
$framedata .= chr($source_data_array['encodingid']);
|
||||
$framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
|
||||
$framedata .= chr($source_data_array['picturetypeid']);
|
||||
$framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
|
||||
$framedata .= (!empty($source_data_array['description']) ? $source_data_array['description'] : '').getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
|
||||
$framedata .= $source_data_array['data'];
|
||||
}
|
||||
break;
|
||||
|
@ -1146,7 +1138,9 @@ class getid3_write_id3v2
|
|||
break;
|
||||
|
||||
default:
|
||||
if ($frame_name{0} == 'T') {
|
||||
if ((($this->majorversion == 2) && (strlen($frame_name) != 3)) || (($this->majorversion > 2) && (strlen($frame_name) != 4))) {
|
||||
$this->errors[] = 'Invalid frame name "'.$frame_name.'" for ID3v2.'.$this->majorversion;
|
||||
} elseif ($frame_name{0} == 'T') {
|
||||
// 4.2. T??? Text information frames
|
||||
// Text encoding $xx
|
||||
// Information <text string(s) according to encoding>
|
||||
|
@ -1624,7 +1618,9 @@ class getid3_write_id3v2
|
|||
if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
|
||||
// pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength
|
||||
// "Furthermore it MUST NOT have any padding when a tag footer is added to the tag."
|
||||
$tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
|
||||
if (($this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion)) > 0) {
|
||||
$tagstring .= str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
|
||||
}
|
||||
}
|
||||
if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) {
|
||||
// special unsynchronisation case:
|
||||
|
@ -1822,7 +1818,7 @@ class getid3_write_id3v2
|
|||
// hashes -> merge based on keys
|
||||
$keys = array_merge(array_keys($arr1), array_keys($arr2));
|
||||
foreach ($keys as $key) {
|
||||
$new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]);
|
||||
$new_array[$key] = $this->array_join_merge((isset($arr1[$key]) ? $arr1[$key] : ''), (isset($arr2[$key]) ? $arr2[$key] : ''));
|
||||
}
|
||||
} else {
|
||||
// two real arrays -> merge
|
||||
|
@ -1880,15 +1876,15 @@ 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 (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($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 (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
|
||||
} elseif (!preg_match('#^([[:alnum:]-]|[\\_])*$#i', $parts['user'], $regs)) {
|
||||
return false;
|
||||
} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
|
||||
} elseif (!preg_match('#^([[:alnum:]-]|[\\_])*$#i', $parts['pass'], $regs)) {
|
||||
return false;
|
||||
} elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
|
||||
} elseif (!preg_match('#^[[:alnum:]/_\\.@~-]*$#i', $parts['path'], $regs)) {
|
||||
return false;
|
||||
} elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
|
||||
} elseif (!empty($parts['query']) && !preg_match('#^[[:alnum:]?&=+:;_()%\\#/,\\.-]*$#i', $parts['query'], $regs)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
@ -1897,7 +1893,7 @@ class getid3_write_id3v2
|
|||
return false;
|
||||
}
|
||||
|
||||
function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
|
||||
static function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
|
||||
$long_description = str_replace(' ', '_', strtolower(trim($long_description)));
|
||||
static $ID3v2ShortFrameNameLookup = array();
|
||||
if (empty($ID3v2ShortFrameNameLookup)) {
|
||||
|
@ -2045,7 +2041,7 @@ class getid3_write_id3v2
|
|||
$ID3v2ShortFrameNameLookup[4]['title_sort_order'] = 'TSOT';
|
||||
$ID3v2ShortFrameNameLookup[4]['set_subtitle'] = 'TSST';
|
||||
}
|
||||
return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)];
|
||||
return (isset($ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)]) ? $ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)] : '');
|
||||
|
||||
}
|
||||
|
|
@ -30,13 +30,12 @@ class getid3_write_lyrics3
|
|||
$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
|
||||
return false;
|
||||
}
|
||||
|
||||
function DeleteLyrics3() {
|
||||
// Initialize getID3 engine
|
||||
$getID3 = new getID3;
|
||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||
if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
||||
if ($fp = @fopen($this->filename, 'a+b')) {
|
||||
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);
|
||||
|
@ -61,18 +60,14 @@ class getid3_write_lyrics3
|
|||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
|
||||
$this->errors[] = 'Cannot fopen('.$this->filename.', "a+b")';
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
// no Lyrics3 present
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,163 @@
|
|||
<?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 //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.metaflac.php //
|
||||
// module for writing metaflac tags //
|
||||
// dependencies: /helperapps/metaflac.exe //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_write_metaflac
|
||||
{
|
||||
|
||||
var $filename;
|
||||
var $tag_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_metaflac() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteMetaFLAC() {
|
||||
|
||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create file with new comments
|
||||
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
||||
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");
|
||||
}
|
||||
}
|
||||
fclose($fpcomments);
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$tempcommentsfilename.'", "wb")';
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||
//$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
|
||||
// metaflac works fine if you copy-paste the above commandline into a command prompt,
|
||||
// but refuses to work with `backtick` if there are "doublequotes" present around BOTH
|
||||
// the metaflac pathname and the target filename. For whatever reason...??
|
||||
// The solution is simply ensure that the metaflac pathname has no spaces,
|
||||
// and therefore does not need to be quoted
|
||||
|
||||
// On top of that, if error messages are not always captured properly under Windows
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
if (empty($metaflacError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// It's simpler on *nix
|
||||
$commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
// Remove temporary comments file
|
||||
unlink($tempcommentsfilename);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($metaflacError)) {
|
||||
|
||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function DeleteMetaFLAC() {
|
||||
|
||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
if (empty($metaflacError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// It's simpler on *nix
|
||||
$commandline = 'metaflac --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||
$metaflacError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($metaflacError)) {
|
||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function CleanmetaflacName($originalcommentname) {
|
||||
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
|
||||
// 0x7A inclusive (a-z).
|
||||
|
||||
// replace invalid chars with a space, return uppercase text
|
||||
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
|
||||
// note: *reg_replace() replaces nulls with empty string (not space)
|
||||
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -20,10 +20,10 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
if (!defined('GETID3_INCLUDEPATH')) {
|
||||
die('getid3.php MUST be included before calling getid3_writetags');
|
||||
throw new Exception('getid3.php MUST be included before calling getid3_writetags');
|
||||
}
|
||||
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
|
||||
die('write.php depends on getid3.lib.php, which is missing.');
|
||||
throw new Exception('write.php depends on getid3.lib.php, which is missing.');
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@ class getid3_writetags
|
|||
var $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
|
||||
var $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
|
||||
var $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
|
||||
var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
|
||||
var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
|
||||
var $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
|
||||
|
||||
var $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
|
||||
|
@ -98,7 +98,7 @@ class getid3_writetags
|
|||
$this->ThisFileInfo = $getID3->analyze($this->filename);
|
||||
|
||||
// check for what file types are allowed on this fileformat
|
||||
switch (@$this->ThisFileInfo['fileformat']) {
|
||||
switch (isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : '') {
|
||||
case 'mp3':
|
||||
case 'mp2':
|
||||
case 'mp1':
|
||||
|
@ -119,7 +119,7 @@ class getid3_writetags
|
|||
break;
|
||||
|
||||
case 'ogg':
|
||||
switch (@$this->ThisFileInfo['audio']['dataformat']) {
|
||||
switch (isset($this->ThisFileInfo['audio']['dataformat']) ? $this->ThisFileInfo['audio']['dataformat'] : '') {
|
||||
case 'flac':
|
||||
//$AllowedTagFormats = array('metaflac');
|
||||
$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
|
||||
|
@ -141,10 +141,8 @@ class getid3_writetags
|
|||
}
|
||||
foreach ($this->tagformats as $requested_tag_format) {
|
||||
if (!in_array($requested_tag_format, $AllowedTagFormats)) {
|
||||
$errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat'];
|
||||
if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) {
|
||||
$errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat'];
|
||||
}
|
||||
$errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.(isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : '');
|
||||
$errormessage .= (isset($this->ThisFileInfo['audio']['dataformat']) ? '.'.$this->ThisFileInfo['audio']['dataformat'] : '');
|
||||
$errormessage .= '" files';
|
||||
$this->errors[] = $errormessage;
|
||||
return false;
|
||||
|
@ -216,7 +214,7 @@ class getid3_writetags
|
|||
|
||||
// Validation of supplied data
|
||||
if (!is_array($this->tag_data)) {
|
||||
$this->errors[] = '$tag_data is not an array in WriteTags()';
|
||||
$this->errors[] = '$this->tag_data is not an array in WriteTags()';
|
||||
return false;
|
||||
}
|
||||
// convert supplied data array keys to upper case, if they're not already
|
||||
|
@ -258,7 +256,7 @@ class getid3_writetags
|
|||
if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) {
|
||||
$ape_writer->filename = $this->filename;
|
||||
if (($success = $ape_writer->WriteAPEtag()) === false) {
|
||||
$this->errors[] = 'WriteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteAPEtag() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $ape_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForAPE() failed';
|
||||
|
@ -270,7 +268,7 @@ class getid3_writetags
|
|||
if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) {
|
||||
$id3v1_writer->filename = $this->filename;
|
||||
if (($success = $id3v1_writer->WriteID3v1()) === false) {
|
||||
$this->errors[] = 'WriteID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteID3v1() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v1_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForID3v1() failed';
|
||||
|
@ -286,7 +284,7 @@ class getid3_writetags
|
|||
if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
|
||||
$id3v2_writer->filename = $this->filename;
|
||||
if (($success = $id3v2_writer->WriteID3v2()) === false) {
|
||||
$this->errors[] = 'WriteID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteID3v2() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v2_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForID3v2() failed';
|
||||
|
@ -298,7 +296,7 @@ class getid3_writetags
|
|||
if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) {
|
||||
$vorbiscomment_writer->filename = $this->filename;
|
||||
if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) {
|
||||
$this->errors[] = 'WriteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteVorbisComment() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $vorbiscomment_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForVorbisComment() failed';
|
||||
|
@ -310,7 +308,7 @@ class getid3_writetags
|
|||
if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) {
|
||||
$metaflac_writer->filename = $this->filename;
|
||||
if (($success = $metaflac_writer->WriteMetaFLAC()) === false) {
|
||||
$this->errors[] = 'WriteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteMetaFLAC() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $metaflac_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForMetaFLAC() failed';
|
||||
|
@ -322,7 +320,7 @@ class getid3_writetags
|
|||
if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) {
|
||||
$real_writer->filename = $this->filename;
|
||||
if (($success = $real_writer->WriteReal()) === false) {
|
||||
$this->errors[] = 'WriteReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
|
||||
$this->errors[] = 'WriteReal() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $real_writer->errors)))).'</li></ul></pre>';
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = 'FormatDataForReal() failed';
|
||||
|
@ -421,6 +419,7 @@ class getid3_writetags
|
|||
if ($this->overwrite_tags) {
|
||||
// do nothing - ignore previous data
|
||||
} else {
|
||||
throw new Exception('$this->overwrite_tags=false is known to be buggy in this version of getID3. Will be fixed in the near future, check www.getid3.org for a newer version.');
|
||||
if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
|
||||
return false;
|
||||
}
|
||||
|
@ -466,14 +465,12 @@ class getid3_writetags
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
|
||||
$tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
|
||||
$tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM']));
|
||||
$tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR']));
|
||||
$tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
|
||||
|
||||
$tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER'])));
|
||||
$tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
|
||||
$tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
|
||||
$tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM'] ) ? $this->tag_data['ALBUM'] : array())));
|
||||
$tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR'] ) ? $this->tag_data['YEAR'] : array())));
|
||||
$tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
|
||||
$tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACKNUMBER']) ? $this->tag_data['TRACKNUMBER'] : array()))));
|
||||
if ($tag_data_id3v1['track'] <= 0) {
|
||||
$tag_data_id3v1['track'] = '';
|
||||
}
|
||||
|
@ -520,9 +517,35 @@ class getid3_writetags
|
|||
} else {
|
||||
// source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first
|
||||
if ($id3v2_majorversion < 4) {
|
||||
// convert data from other encoding to UTF-16
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value);
|
||||
// convert data from other encoding to UTF-16 (with BOM)
|
||||
// note: some software, notably Windows Media Player and iTunes are broken and treat files tagged with UTF-16BE (with BOM) as corrupt
|
||||
// therefore we force data to UTF-16LE and manually prepend the BOM
|
||||
$ID3v2_tag_data_converted = false;
|
||||
if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'ISO-8859-1')) {
|
||||
// great, leave data as-is for minimum compatability problems
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0;
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
|
||||
$ID3v2_tag_data_converted = true;
|
||||
}
|
||||
if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'UTF-8')) {
|
||||
do {
|
||||
// if UTF-8 string does not include any characters above chr(127) then it is identical to ISO-8859-1
|
||||
for ($i = 0; $i < strlen($value); $i++) {
|
||||
if (ord($value{$i}) > 127) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0;
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
|
||||
$ID3v2_tag_data_converted = true;
|
||||
} while (false);
|
||||
}
|
||||
if (!$ID3v2_tag_data_converted) {
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
|
||||
//$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value); // output is UTF-16LE+BOM or UTF-16BE+BOM depending on system architecture
|
||||
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = "\xFF\xFE".getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16LE', $value); // force LittleEndian order version of UTF-16
|
||||
$ID3v2_tag_data_converted = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// convert data from other encoding to UTF-8
|
||||
|
@ -578,10 +601,10 @@ class getid3_writetags
|
|||
}
|
||||
|
||||
function FormatDataForReal() {
|
||||
$tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
|
||||
$tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
|
||||
$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT']));
|
||||
$tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
|
||||
$tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
|
||||
$tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
|
||||
$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COPYRIGHT']) ? $this->tag_data['COPYRIGHT'] : array())));
|
||||
$tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
|
||||
|
||||
$this->MergeExistingTagData('real', $tag_data_real);
|
||||
return $tag_data_real;
|
|
@ -0,0 +1,275 @@
|
|||
<?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 //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.real.php //
|
||||
// module for writing RealAudio/RealVideo tags //
|
||||
// dependencies: module.tag.real.php //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
class getid3_write_real
|
||||
{
|
||||
var $filename;
|
||||
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;
|
||||
}
|
||||
|
||||
function WriteReal() {
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
||||
|
||||
// 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[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
||||
return false;
|
||||
}
|
||||
|
||||
function GenerateRMFchunk(&$chunks) {
|
||||
$oldCONTexists = false;
|
||||
foreach ($chunks as $key => $chunk) {
|
||||
$chunkNameKeys[$chunk['name']] = $key;
|
||||
if ($chunk['name'] == 'CONT') {
|
||||
$oldCONTexists = true;
|
||||
}
|
||||
}
|
||||
$newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
|
||||
|
||||
$RMFchunk = "\x00\x00"; // object version
|
||||
$RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
|
||||
$RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4);
|
||||
|
||||
$RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
|
||||
return $RMFchunk;
|
||||
}
|
||||
|
||||
function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
|
||||
$old_CONT_length = 0;
|
||||
$old_DATA_offset = 0;
|
||||
$old_INDX_offset = 0;
|
||||
foreach ($chunks as $key => $chunk) {
|
||||
$chunkNameKeys[$chunk['name']] = $key;
|
||||
if ($chunk['name'] == 'CONT') {
|
||||
$old_CONT_length = $chunk['length'];
|
||||
} elseif ($chunk['name'] == 'DATA') {
|
||||
if (!$old_DATA_offset) {
|
||||
$old_DATA_offset = $chunk['offset'];
|
||||
}
|
||||
} elseif ($chunk['name'] == 'INDX') {
|
||||
if (!$old_INDX_offset) {
|
||||
$old_INDX_offset = $chunk['offset'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
|
||||
|
||||
$PROPchunk = "\x00\x00"; // object version
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
|
||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
|
||||
|
||||
$PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
|
||||
return $PROPchunk;
|
||||
}
|
||||
|
||||
function GenerateCONTchunk() {
|
||||
foreach ($this->tag_data as $key => $value) {
|
||||
// limit each value to 0xFFFF bytes
|
||||
$this->tag_data[$key] = substr($value, 0, 65535);
|
||||
}
|
||||
|
||||
$CONTchunk = "\x00\x00"; // object version
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0), 2);
|
||||
$CONTchunk .= (!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '');
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0), 2);
|
||||
$CONTchunk .= (!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '');
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
|
||||
$CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
|
||||
|
||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0), 2);
|
||||
$CONTchunk .= (!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '');
|
||||
|
||||
if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
|
||||
$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
|
||||
}
|
||||
|
||||
$CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
|
||||
|
||||
return $CONTchunk;
|
||||
}
|
||||
|
||||
function RemoveReal() {
|
||||
// File MUST be writeable - CHMOD(646) at least
|
||||
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
||||
|
||||
// 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')) {
|
||||
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[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,121 @@
|
|||
<?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 //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// write.vorbiscomment.php //
|
||||
// module for writing VorbisComment tags //
|
||||
// dependencies: /helperapps/vorbiscomment.exe //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_write_vorbiscomment
|
||||
{
|
||||
|
||||
var $filename;
|
||||
var $tag_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_vorbiscomment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function WriteVorbisComment() {
|
||||
|
||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create file with new comments
|
||||
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
||||
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->CleanVorbisCommentName($key).'='.$commentdata."\n");
|
||||
}
|
||||
}
|
||||
fclose($fpcomments);
|
||||
|
||||
} else {
|
||||
$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldignoreuserabort = ignore_user_abort(true);
|
||||
if (GETID3_OS_ISWINDOWS) {
|
||||
|
||||
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
|
||||
//$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
|
||||
// vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
|
||||
// but refuses to work with `backtick` if there are "doublequotes" present around BOTH
|
||||
// the metaflac pathname and the target filename. For whatever reason...??
|
||||
// The solution is simply ensure that the metaflac pathname has no spaces,
|
||||
// and therefore does not need to be quoted
|
||||
|
||||
// On top of that, if error messages are not always captured properly under Windows
|
||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||
clearstatcache();
|
||||
$timestampbeforewriting = filemtime($this->filename);
|
||||
|
||||
$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||
$VorbiscommentError = `$commandline`;
|
||||
|
||||
if (empty($VorbiscommentError)) {
|
||||
clearstatcache();
|
||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
||||
$VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||
$VorbiscommentError = `$commandline`;
|
||||
|
||||
}
|
||||
|
||||
// Remove temporary comments file
|
||||
unlink($tempcommentsfilename);
|
||||
ignore_user_abort($oldignoreuserabort);
|
||||
|
||||
if (!empty($VorbiscommentError)) {
|
||||
|
||||
$this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DeleteVorbisComment() {
|
||||
$this->tag_data = array(array());
|
||||
return $this->WriteVorbisComment();
|
||||
}
|
||||
|
||||
function CleanVorbisCommentName($originalcommentname) {
|
||||
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
|
||||
// 0x7A inclusive (a-z).
|
||||
|
||||
// replace invalid chars with a space, return uppercase text
|
||||
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
|
||||
// note: *reg_replace() replaces nulls with empty string (not space)
|
||||
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -7,5 +7,4 @@
|
|||
<author>Brice Maron (eMerzh)</author>
|
||||
<require>2</require>
|
||||
<description>Display OwnCloud's dependencies informations (missings modules, ...)</description>
|
||||
<default_enable/>
|
||||
</info>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
require_once('../../../../lib/base.php');
|
||||
OC_JSON::checkLoggedIn();
|
||||
OC_JSON::checkAppEnabled('calendar');
|
||||
|
||||
foreach ($_POST as $key=>$element) {
|
||||
debug('_POST: '.$key.'=>'.print_r($element, true));
|
||||
}
|
||||
|
||||
function bailOut($msg) {
|
||||
OC_JSON::error(array('data' => array('message' => $msg)));
|
||||
OC_Log::write('calendar','ajax/categories/rescan.php: '.$msg, OC_Log::DEBUG);
|
||||
exit();
|
||||
}
|
||||
function debug($msg) {
|
||||
OC_Log::write('calendar','ajax/categories/rescan.php: '.$msg, OC_Log::DEBUG);
|
||||
}
|
||||
|
||||
$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
||||
if(count($calendars) == 0) {
|
||||
bailOut(OC_Calendar_App::$l10n->t('No calendars found.'));
|
||||
}
|
||||
$events = array();
|
||||
foreach($calendars as $calendar) {
|
||||
$calendar_events = OC_Calendar_Object::all($calendar['id']);
|
||||
$events = $events + $calendar_events;
|
||||
}
|
||||
if(count($events) == 0) {
|
||||
bailOut(OC_Calendar_App::$l10n->t('No events found.'));
|
||||
}
|
||||
|
||||
OC_Calendar_App::scanCategories($events);
|
||||
$categories = OC_Calendar_App::getCategoryOptions();
|
||||
|
||||
OC_JSON::success(array('data' => array('categories'=>$categories)));
|
|
@ -47,13 +47,8 @@ switch($dtstart->getDateType()) {
|
|||
|
||||
$summary = $vevent->getAsString('SUMMARY');
|
||||
$location = $vevent->getAsString('LOCATION');
|
||||
$categories = $vevent->getAsArray('CATEGORIES');
|
||||
$categories = $vevent->getAsString('CATEGORIES');
|
||||
$description = $vevent->getAsString('DESCRIPTION');
|
||||
foreach($categories as $category){
|
||||
if (!in_array($category, $category_options)){
|
||||
array_unshift($category_options, $category);
|
||||
}
|
||||
}
|
||||
$last_modified = $vevent->__get('LAST-MODIFIED');
|
||||
if ($last_modified){
|
||||
$lastmodified = $last_modified->getDateTime()->format('U');
|
||||
|
@ -220,7 +215,6 @@ $tmpl->assign('eventid', $id);
|
|||
$tmpl->assign('access', $access);
|
||||
$tmpl->assign('lastmodified', $lastmodified);
|
||||
$tmpl->assign('calendar_options', $calendar_options);
|
||||
$tmpl->assign('category_options', $category_options);
|
||||
$tmpl->assign('repeat_options', $repeat_options);
|
||||
$tmpl->assign('repeat_month_options', $repeat_month_options);
|
||||
$tmpl->assign('repeat_weekly_options', $repeat_weekly_options);
|
||||
|
@ -257,7 +251,14 @@ if($repeat['repeat'] != 'doesnotrepeat'){
|
|||
$tmpl->assign('repeat_bymonthday', $repeat['bymonthday']);
|
||||
$tmpl->assign('repeat_bymonth', $repeat['bymonth']);
|
||||
$tmpl->assign('repeat_byweekno', $repeat['byweekno']);
|
||||
} else {
|
||||
$tmpl->assign('repeat_month', 'monthday');
|
||||
$tmpl->assign('repeat_weekdays', array());
|
||||
$tmpl->assign('repeat_interval', 1);
|
||||
$tmpl->assign('repeat_end', 'never');
|
||||
$tmpl->assign('repeat_count', '10');
|
||||
$tmpl->assign('repeat_weekofmonth', 'auto');
|
||||
$tmpl->assign('repeat_date', '');
|
||||
$tmpl->assign('repeat_year', 'bydate');
|
||||
}
|
||||
$tmpl->printpage();
|
||||
|
||||
?>
|
||||
$tmpl->printpage();
|
|
@ -32,7 +32,6 @@ $start->setTimezone(new DateTimeZone($timezone));
|
|||
$end->setTimezone(new DateTimeZone($timezone));
|
||||
|
||||
$calendar_options = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
||||
$category_options = OC_Calendar_App::getCategoryOptions();
|
||||
$repeat_options = OC_Calendar_App::getRepeatOptions();
|
||||
$repeat_end_options = OC_Calendar_App::getEndOptions();
|
||||
$repeat_month_options = OC_Calendar_App::getMonthOptions();
|
||||
|
@ -47,7 +46,6 @@ $repeat_bymonthday_options = OC_Calendar_App::getByMonthDayOptions();
|
|||
$tmpl = new OC_Template('calendar', 'part.newevent');
|
||||
$tmpl->assign('access', 'owner');
|
||||
$tmpl->assign('calendar_options', $calendar_options);
|
||||
$tmpl->assign('category_options', $category_options);
|
||||
$tmpl->assign('repeat_options', $repeat_options);
|
||||
$tmpl->assign('repeat_month_options', $repeat_month_options);
|
||||
$tmpl->assign('repeat_weekly_options', $repeat_weekly_options);
|
||||
|
@ -74,4 +72,3 @@ $tmpl->assign('repeat_weekofmonth', 'auto');
|
|||
$tmpl->assign('repeat_date', '');
|
||||
$tmpl->assign('repeat_year', 'bydate');
|
||||
$tmpl->printpage();
|
||||
?>
|
||||
|
|
|
@ -26,6 +26,7 @@ $eventSources[] = array('url' => 'ajax/events.php?calendar_id=shared_rw', 'backg
|
|||
$eventSources[] = array('url' => 'ajax/events.php?calendar_id=shared_r', 'backgroundColor' => '#1D2D44', 'borderColor' => '#888', 'textColor' => 'white', 'editable' => 'false');
|
||||
|
||||
OC_Hook::emit('OC_Calendar', 'getSources', array('sources' => &$eventSources));
|
||||
$categories = OC_Calendar_App::getCategoryOptions();
|
||||
|
||||
//Fix currentview for fullcalendar
|
||||
if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'month') == "oneweekview"){
|
||||
|
@ -49,9 +50,12 @@ OC_Util::addScript('calendar', 'calendar');
|
|||
OC_Util::addStyle('calendar', 'style');
|
||||
OC_Util::addScript('', 'jquery.multiselect');
|
||||
OC_Util::addStyle('', 'jquery.multiselect');
|
||||
OC_Util::addScript('contacts','jquery.multi-autocomplete');
|
||||
OC_Util::addScript('','oc-vcategories');
|
||||
OC_App::setActiveNavigationEntry('calendar_index');
|
||||
$tmpl = new OC_Template('calendar', 'calendar', 'user');
|
||||
$tmpl->assign('eventSources', $eventSources);
|
||||
$tmpl->assign('categories', $categories);
|
||||
if(array_key_exists('showevent', $_GET)){
|
||||
$tmpl->assign('showevent', $_GET['showevent']);
|
||||
}
|
||||
|
|
|
@ -32,13 +32,7 @@ Calendar={
|
|||
$('#totime').timepicker({
|
||||
showPeriodLabels: false
|
||||
});
|
||||
$('#category').multiselect({
|
||||
header: false,
|
||||
noneSelectedText: $('#category').attr('title'),
|
||||
selectedList: 2,
|
||||
minWidth:'auto',
|
||||
classes: 'category',
|
||||
});
|
||||
$('#category').multiple_autocomplete({source: categories});
|
||||
Calendar.UI.repeat('init');
|
||||
$('#end').change(function(){
|
||||
Calendar.UI.repeat('end');
|
||||
|
@ -371,6 +365,11 @@ Calendar={
|
|||
}
|
||||
$('#'+id).addClass('active');
|
||||
},
|
||||
categoriesChanged:function(newcategories){
|
||||
categories = $.map(newcategories, function(v) {return v;});
|
||||
console.log('Calendar categories changed to: ' + categories);
|
||||
$('#category').multiple_autocomplete('option', 'source', categories);
|
||||
},
|
||||
Calendar:{
|
||||
overview:function(){
|
||||
if($('#choosecalendar_dialog').dialog('isOpen') == true){
|
||||
|
@ -833,6 +832,8 @@ $(document).ready(function(){
|
|||
loading: Calendar.UI.loading,
|
||||
eventSources: eventSources
|
||||
});
|
||||
OCCategories.changed = Calendar.UI.categoriesChanged;
|
||||
OCCategories.app = 'calendar';
|
||||
$('#oneweekview_radio').click(function(){
|
||||
$('#calendar_holder').fullCalendar('changeView', 'agendaWeek');
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@ class OC_Calendar_App{
|
|||
* @brief language object for calendar app
|
||||
*/
|
||||
public static $l10n;
|
||||
protected static $categories = null;
|
||||
|
||||
/*
|
||||
* @brief timezone of the user
|
||||
|
@ -106,19 +107,76 @@ class OC_Calendar_App{
|
|||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* THIS FUNCTION IS DEPRECATED AND WILL BE REMOVED SOON
|
||||
* @brief returns the valid categories
|
||||
* @return array - categories
|
||||
*/
|
||||
public static function getCategoryOptions(){
|
||||
return OC_Calendar_Object::getCategoryOptions(self::$l10n);
|
||||
|
||||
protected static function getDefaultCategories()
|
||||
{
|
||||
return array(
|
||||
self::$l10n->t('Birthday'),
|
||||
self::$l10n->t('Business'),
|
||||
self::$l10n->t('Call'),
|
||||
self::$l10n->t('Clients'),
|
||||
self::$l10n->t('Deliverer'),
|
||||
self::$l10n->t('Holidays'),
|
||||
self::$l10n->t('Ideas'),
|
||||
self::$l10n->t('Journey'),
|
||||
self::$l10n->t('Jubilee'),
|
||||
self::$l10n->t('Meeting'),
|
||||
self::$l10n->t('Other'),
|
||||
self::$l10n->t('Personal'),
|
||||
self::$l10n->t('Projects'),
|
||||
self::$l10n->t('Questions'),
|
||||
self::$l10n->t('Work'),
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief returns the options for an repeating event
|
||||
* @return array - valid inputs for repeating events
|
||||
|
||||
protected static function getVCategories() {
|
||||
if (is_null(self::$categories)) {
|
||||
self::$categories = new OC_VCategories('calendar', null, self::getDefaultCategories());
|
||||
}
|
||||
return self::$categories;
|
||||
}
|
||||
|
||||
public static function getCategoryOptions()
|
||||
{
|
||||
$categories = self::getVCategories()->categories();
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* scan events for categories.
|
||||
* @param $events VEVENTs to scan. null to check all events for the current user.
|
||||
*/
|
||||
public static function scanCategories($events = null) {
|
||||
if (is_null($events)) {
|
||||
$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
||||
if(count($calendars) > 0) {
|
||||
$events = array();
|
||||
foreach($calendars as $calendar) {
|
||||
$calendar_events = OC_Calendar_Object::all($calendar['id']);
|
||||
$events = $events + $calendar_events;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(is_array($events) && count($events) > 0) {
|
||||
$vcategories = self::getVCategories();
|
||||
$vcategories->delete($vcategories->categories());
|
||||
foreach($events as $event) {
|
||||
$vobject = OC_VObject::parse($event['calendardata']);
|
||||
if(!is_null($vobject)) {
|
||||
$vcategories->loadFromVObject($vobject->VEVENT, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check VEvent for new categories.
|
||||
* @see OC_VCategories::loadFromVObject
|
||||
*/
|
||||
public static function loadCategoriesFromVCalendar(OC_VObject $calendar) {
|
||||
self::getVCategories()->loadFromVObject($calendar->VEVENT, true);
|
||||
}
|
||||
|
||||
public static function getRepeatOptions(){
|
||||
return OC_Calendar_Object::getRepeatOptions(self::$l10n);
|
||||
}
|
||||
|
@ -244,7 +302,6 @@ class OC_Calendar_App{
|
|||
* @return (array) $events
|
||||
*/
|
||||
public static function getrequestedEvents($calendarid, $start, $end){
|
||||
|
||||
$events = array();
|
||||
if($calendarid == 'shared_rw' || $_GET['calendar_id'] == 'shared_r'){
|
||||
$calendars = OC_Calendar_Share::allSharedwithuser(OC_USER::getUser(), OC_Calendar_Share::CALENDAR, 1, ($_GET['calendar_id'] == 'shared_rw')?'rw':'r');
|
||||
|
|
|
@ -44,10 +44,8 @@ class OC_Calendar_Calendar{
|
|||
/**
|
||||
* @brief Returns the list of calendars for a specific user.
|
||||
* @param string $uid User ID
|
||||
* @param boolean $active
|
||||
* @param boolean $active Only return calendars with this $active state, default(=null) is don't care
|
||||
* @return array
|
||||
*
|
||||
* TODO: what is active for?
|
||||
*/
|
||||
public static function allCalendars($uid, $active=null){
|
||||
$values = array($uid);
|
||||
|
|
|
@ -93,6 +93,7 @@ class OC_Calendar_Object{
|
|||
*/
|
||||
public static function add($id,$data){
|
||||
$object = OC_VObject::parse($data);
|
||||
OC_Calendar_App::loadCategoriesFromVCalendar($object);
|
||||
list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
|
||||
|
||||
if(is_null($uid)){
|
||||
|
@ -139,6 +140,7 @@ class OC_Calendar_Object{
|
|||
$oldobject = self::find($id);
|
||||
|
||||
$object = OC_VObject::parse($data);
|
||||
OC_Calendar_App::loadCategoriesFromVCalendar($object);
|
||||
list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
|
||||
|
||||
$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_objects SET objecttype=?,startdate=?,enddate=?,repeating=?,summary=?,calendardata=?, lastmodified = ? WHERE id = ?' );
|
||||
|
@ -320,27 +322,6 @@ class OC_Calendar_Object{
|
|||
return $dtend;
|
||||
}
|
||||
|
||||
public static function getCategoryOptions($l10n)
|
||||
{
|
||||
return array(
|
||||
$l10n->t('Birthday'),
|
||||
$l10n->t('Business'),
|
||||
$l10n->t('Call'),
|
||||
$l10n->t('Clients'),
|
||||
$l10n->t('Deliverer'),
|
||||
$l10n->t('Holidays'),
|
||||
$l10n->t('Ideas'),
|
||||
$l10n->t('Journey'),
|
||||
$l10n->t('Jubilee'),
|
||||
$l10n->t('Meeting'),
|
||||
$l10n->t('Other'),
|
||||
$l10n->t('Personal'),
|
||||
$l10n->t('Projects'),
|
||||
$l10n->t('Questions'),
|
||||
$l10n->t('Work'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getRepeatOptions($l10n)
|
||||
{
|
||||
return array(
|
||||
|
@ -452,10 +433,6 @@ class OC_Calendar_Object{
|
|||
$errnum++;
|
||||
}
|
||||
|
||||
if(isset($request['categories']) && !is_array($request['categories'])){
|
||||
$errarr['categories'] = $l10n->t('Not an array');
|
||||
}
|
||||
|
||||
$fromday = substr($request['from'], 0, 2);
|
||||
$frommonth = substr($request['from'], 3, 2);
|
||||
$fromyear = substr($request['from'], 6, 4);
|
||||
|
@ -623,7 +600,7 @@ class OC_Calendar_Object{
|
|||
{
|
||||
$title = $request["title"];
|
||||
$location = $request["location"];
|
||||
$categories = isset($request["categories"]) ? $request["categories"] : array();
|
||||
$categories = $request["categories"];
|
||||
$allday = isset($request["allday"]);
|
||||
$from = $request["from"];
|
||||
$to = $request["to"];
|
||||
|
@ -795,7 +772,7 @@ class OC_Calendar_Object{
|
|||
|
||||
$vevent->setString('LOCATION', $location);
|
||||
$vevent->setString('DESCRIPTION', $description);
|
||||
$vevent->setString('CATEGORIES', join(',', $categories));
|
||||
$vevent->setString('CATEGORIES', $categories);
|
||||
|
||||
/*if($repeat == "true"){
|
||||
$vevent->RRULE = $repeat;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script type='text/javascript'>
|
||||
var defaultView = '<?php echo OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'month') ?>';
|
||||
var eventSources = <?php echo json_encode($_['eventSources']) ?>;
|
||||
var categories = <?php echo json_encode($_['categories']); ?>;
|
||||
var dayNames = <?php echo json_encode($l->tA(array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'))) ?>;
|
||||
var dayNamesShort = <?php echo json_encode($l->tA(array('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'))) ?>;
|
||||
var monthNames = <?php echo json_encode($l->tA(array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))) ?>;
|
||||
|
|
|
@ -26,12 +26,8 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid
|
|||
<tr>
|
||||
<th width="75px"><?php echo $l->t("Category");?>:</th>
|
||||
<td>
|
||||
<select id="category" name="categories[]" multiple="multiple" title="<?php echo $l->t("Select category") ?>">
|
||||
<?php
|
||||
if (!isset($_['categories'])) {$_['categories'] = array();}
|
||||
echo html_select_options($_['category_options'], $_['categories'], array('combine'=>true));
|
||||
?>
|
||||
</select>
|
||||
<input id="category" name="categories" type="text" placeholder="<?php echo $l->t('Separate categories with commas'); ?>" value="<?php echo isset($_['categories']) ? htmlspecialchars($_['categories']) : '' ?>">
|
||||
<a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"><img alt="<?php echo $l->t('Edit categories'); ?>" src="<?php echo image_path('core','actions/rename.svg')?>" class="svg action" style="width: 16px; height: 16px;"></a>
|
||||
</td>
|
||||
<?php if(count($_['calendar_options']) > 1) { ?>
|
||||
<th width="75px"> <?php echo $l->t("Calendar");?>:</th>
|
||||
|
@ -253,4 +249,4 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid
|
|||
<div id="tabs-5">
|
||||
<?php echo $this->inc('share.dropdown'); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
|
|
|
@ -10,7 +10,7 @@ require_once('../../../../lib/base.php');
|
|||
OC_JSON::checkLoggedIn();
|
||||
OC_JSON::checkAppEnabled('contacts');
|
||||
|
||||
$categories = OC_Contacts_App::$categories->categories();
|
||||
$categories = OC_Contacts_App::getCategories();
|
||||
|
||||
OC_JSON::success(array('data' => array('categories'=>$categories)));
|
||||
|
||||
|
|
|
@ -36,13 +36,8 @@ if(count($contacts) == 0) {
|
|||
bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
|
||||
}
|
||||
|
||||
$cards = array();
|
||||
foreach($contacts as $contact) {
|
||||
$cards[] = $contact['carddata'];
|
||||
}
|
||||
|
||||
OC_Contacts_App::$categories->rescan($cards);
|
||||
$categories = OC_Contacts_App::$categories->categories();
|
||||
OC_Contacts_App::scanCategories($contacts);
|
||||
$categories = OC_Contacts_App::getCategories();
|
||||
|
||||
OC_JSON::success(array('data' => array('categories'=>$categories)));
|
||||
|
||||
|
|
|
@ -35,25 +35,6 @@ if(!is_null($id)) {
|
|||
$property_types = OC_Contacts_App::getAddPropertyOptions();
|
||||
$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
|
||||
$categories = OC_Contacts_App::getCategories();
|
||||
if(count($categories) == 0) {
|
||||
$vcaddressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
|
||||
if(count($vcaddressbooks) > 0) {
|
||||
$vcaddressbookids = array();
|
||||
foreach($vcaddressbooks as $vcaddressbook) {
|
||||
$vcaddressbookids[] = $vcaddressbook['id'];
|
||||
}
|
||||
$vccontacts = OC_Contacts_VCard::all($vcaddressbookids);
|
||||
if(count($vccontacts) > 0) {
|
||||
$cards = array();
|
||||
foreach($vccontacts as $vccontact) {
|
||||
$cards[] = $vccontact['carddata'];
|
||||
}
|
||||
|
||||
OC_Contacts_App::$categories->rescan($cards);
|
||||
$categories = OC_Contacts_App::$categories->categories();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize'));
|
||||
$post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size'));
|
||||
|
@ -71,7 +52,6 @@ OC_Util::addScript('contacts','jquery.inview');
|
|||
OC_Util::addScript('contacts','jquery.Jcrop');
|
||||
OC_Util::addScript('contacts','jquery.multi-autocomplete');
|
||||
OC_Util::addStyle('','jquery.multiselect');
|
||||
OC_Util::addStyle('','oc-vcategories');
|
||||
OC_Util::addStyle('contacts','jquery.combobox');
|
||||
OC_Util::addStyle('contacts','jquery.Jcrop');
|
||||
OC_Util::addStyle('contacts','contacts');
|
||||
|
@ -79,9 +59,9 @@ OC_Util::addStyle('contacts','contacts');
|
|||
$tmpl = new OC_Template( "contacts", "index", "user" );
|
||||
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
|
||||
$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
|
||||
$tmpl->assign('property_types',$property_types);
|
||||
$tmpl->assign('phone_types',$phone_types);
|
||||
$tmpl->assign('categories',$categories);
|
||||
$tmpl->assign('property_types', $property_types);
|
||||
$tmpl->assign('phone_types', $phone_types);
|
||||
$tmpl->assign('categories', $categories);
|
||||
$tmpl->assign('addressbooks', $addressbooks);
|
||||
$tmpl->assign('contacts', $contacts);
|
||||
$tmpl->assign('details', $details );
|
||||
|
|
|
@ -504,8 +504,9 @@ Contacts={
|
|||
return false;
|
||||
},
|
||||
categoriesChanged:function(newcategories) { // Categories added/deleted.
|
||||
console.log('categoriesChanged for ' + Contacts.UI.Card.id + ' : ' + newcategories);
|
||||
categories = newcategories;
|
||||
categories = $.map(newcategories, function(v) {return v;});
|
||||
console.log('categoriesChanged for ' + Contacts.UI.Card.id + ' : ' + categories);
|
||||
$('#categories').multiple_autocomplete('option', 'source', categories);
|
||||
var categorylist = $('#categories_value').find('input');
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'categories/categoriesfor.php'),{'id':Contacts.UI.Card.id},function(jsondata){
|
||||
if(jsondata.status == 'success'){
|
||||
|
|
|
@ -53,11 +53,12 @@ class OC_Contacts_App {
|
|||
if( $addressbook === false || $addressbook['userid'] != OC_User::getUser()) {
|
||||
if ($addressbook === false) {
|
||||
OC_Log::write('contacts', 'Addressbook not found: '. $id, OC_Log::ERROR);
|
||||
OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('Addressbook not found.'))));
|
||||
}
|
||||
else {
|
||||
OC_Log::write('contacts', 'Addressbook('.$id.') is not from '.OC_User::getUser(), OC_Log::ERROR);
|
||||
OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('This is not your addressbook.'))));
|
||||
}
|
||||
OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('This is not your addressbook.')))); // Same here (as with the contact error). Could this error be improved?
|
||||
exit();
|
||||
}
|
||||
return $addressbook;
|
||||
|
@ -156,7 +157,45 @@ class OC_Contacts_App {
|
|||
}
|
||||
|
||||
public static function getCategories() {
|
||||
return self::$categories->categories();
|
||||
$categories = self::$categories->categories();
|
||||
if(count($categories) == 0) {
|
||||
self::scanCategories();
|
||||
$categories = self::$categories->categories();
|
||||
}
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* scan vcards for categories.
|
||||
* @param $vccontacts VCards to scan. null to check all vcards for the current user.
|
||||
*/
|
||||
public static function scanCategories($vccontacts = null) {
|
||||
if (is_null($vccontacts)) {
|
||||
$vcaddressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
|
||||
if(count($vcaddressbooks) > 0) {
|
||||
$vcaddressbookids = array();
|
||||
foreach($vcaddressbooks as $vcaddressbook) {
|
||||
$vcaddressbookids[] = $vcaddressbook['id'];
|
||||
}
|
||||
$vccontacts = OC_Contacts_VCard::all($vcaddressbookids);
|
||||
}
|
||||
}
|
||||
if(is_array($vccontacts) && count($vccontacts) > 0) {
|
||||
$cards = array();
|
||||
foreach($vccontacts as $vccontact) {
|
||||
$cards[] = $vccontact['carddata'];
|
||||
}
|
||||
|
||||
self::$categories->rescan($cards);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check VCard for new categories.
|
||||
* @see OC_VCategories::loadFromVObject
|
||||
*/
|
||||
public static function loadCategoriesFromVCard(OC_VObject $contact) {
|
||||
self::$categories->loadFromVObject($contact, true);
|
||||
}
|
||||
|
||||
public static function setLastModifiedHeader($contact) {
|
||||
|
|
|
@ -148,7 +148,7 @@ class OC_Contacts_VCard{
|
|||
$stringprops = array('N', 'FN', 'ORG', 'NICK', 'ADR', 'NOTE');
|
||||
$typeprops = array('ADR', 'TEL', 'EMAIL');
|
||||
$upgrade = false;
|
||||
$fn = $n = $uid = $email = null;
|
||||
$fn = $n = $uid = $email = $org = null;
|
||||
$version = $vcard->getAsString('VERSION');
|
||||
// Add version if needed
|
||||
if($version && $version < '3.0') {
|
||||
|
@ -234,7 +234,7 @@ class OC_Contacts_VCard{
|
|||
return null;
|
||||
};
|
||||
|
||||
OC_Contacts_App::$categories->loadFromVObject($card);
|
||||
OC_Contacts_App::loadCategoriesFromVCard($card);
|
||||
|
||||
self::updateValuesFromAdd($card);
|
||||
|
||||
|
@ -306,7 +306,7 @@ class OC_Contacts_VCard{
|
|||
return false;
|
||||
}
|
||||
|
||||
OC_Contacts_App::$categories->loadFromVObject($card);
|
||||
OC_Contacts_App::loadCategoriesFromVCard($card);
|
||||
|
||||
$fn = $card->getAsString('FN');
|
||||
if (empty($fn)) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script type='text/javascript'>
|
||||
var totalurl = '<?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/addressbooks';
|
||||
var categories = <?php sort($_['categories']); echo json_encode($_['categories']); ?>;
|
||||
var categories = <?php echo json_encode($_['categories']); ?>;
|
||||
var lang = '<?php echo OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang', 'en'); ?>';
|
||||
</script>
|
||||
<div id="controls">
|
||||
|
|
|
@ -14,7 +14,7 @@ $id = isset($_['id']) ? $_['id'] : '';
|
|||
<li><a data-type="EMAIL"><?php echo $l->t('Email'); ?></a></li>
|
||||
<li><a data-type="ADR"><?php echo $l->t('Address'); ?></a></li>
|
||||
<li><a data-type="NOTE"><?php echo $l->t('Note'); ?></a></li>
|
||||
<li><a data-type="CATEGORIES"><?php echo $l->t('Categories'); ?></a></li>
|
||||
<li><a data-type="CATEGORIES"><?php echo $l->t('Groups'); ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<img onclick="Contacts.UI.Card.doExport();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
|
||||
|
@ -58,7 +58,7 @@ $id = isset($_['id']) ? $_['id'] : '';
|
|||
<dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property big" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
|
||||
<dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Groups'); ?></label></dt>
|
||||
<dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property bold" name="value" value="" placeholder="
|
||||
<?php echo $l->t('Separate groups with commas'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd>
|
||||
<?php echo $l->t('Separate groups with commas'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit groups'); ?>"></a></dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
<?php
|
||||
return array(
|
||||
'ftp'=>array(
|
||||
'run'=>false,
|
||||
'host'=>'localhost',
|
||||
'user'=>'test',
|
||||
'password'=>'test',
|
||||
'root'=>'/test',
|
||||
),
|
||||
'webdav'=>array(
|
||||
'run'=>false,
|
||||
'host'=>'localhost',
|
||||
'user'=>'test',
|
||||
'password'=>'test',
|
||||
'root'=>'/owncloud/files/webdav.php',
|
||||
),
|
||||
'google'=>array(
|
||||
'run'=>false,
|
||||
'consumer_key'=>'anonymous',
|
||||
'consumer_secret'=>'anonymous',
|
||||
'token'=>'test',
|
||||
|
|
|
@ -5,19 +5,26 @@
|
|||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
$config=include('apps/files_external/tests/config.php');
|
||||
if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']){
|
||||
abstract class Test_Filestorage_FTP extends Test_FileStorage{}
|
||||
return;
|
||||
}else{
|
||||
class Test_Filestorage_FTP extends Test_FileStorage {
|
||||
private $config;
|
||||
private $id;
|
||||
|
||||
class Test_Filestorage_FTP extends Test_FileStorage {
|
||||
private $config;
|
||||
private $id;
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_FTP($this->config['ftp']);
|
||||
}
|
||||
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_FTP($this->config['ftp']);
|
||||
}
|
||||
|
||||
public function tearDown(){
|
||||
OC_Helper::rmdirr($this->instance->constructUrl(''));
|
||||
public function tearDown(){
|
||||
OC_Helper::rmdirr($this->instance->constructUrl(''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,19 +20,26 @@
|
|||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class Test_Filestorage_Google extends Test_FileStorage {
|
||||
|
||||
private $config;
|
||||
private $id;
|
||||
$config=include('apps/files_external/tests/config.php');
|
||||
if(!is_array($config) or !isset($config['google']) or !$config['google']['run']){
|
||||
abstract class Test_Filestorage_Google extends Test_FileStorage{}
|
||||
return;
|
||||
}else{
|
||||
class Test_Filestorage_Google extends Test_FileStorage {
|
||||
|
||||
private $config;
|
||||
private $id;
|
||||
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_Google($this->config['google']);
|
||||
}
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_Google($this->config['google']);
|
||||
}
|
||||
|
||||
public function tearDown(){
|
||||
$this->instance->rmdir('/');
|
||||
public function tearDown(){
|
||||
$this->instance->rmdir('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,18 +6,25 @@
|
|||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
class Test_Filestorage_DAV extends Test_FileStorage {
|
||||
private $config;
|
||||
private $id;
|
||||
$config=include('apps/files_external/tests/config.php');
|
||||
if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run']){
|
||||
abstract class Test_Filestorage_DAV extends Test_FileStorage{}
|
||||
return;
|
||||
}else{
|
||||
class Test_Filestorage_DAV extends Test_FileStorage {
|
||||
private $config;
|
||||
private $id;
|
||||
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_DAV($this->config['webdav']);
|
||||
}
|
||||
public function setUp(){
|
||||
$id=uniqid();
|
||||
$this->config=include('apps/files_external/tests/config.php');
|
||||
$this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in
|
||||
$this->instance=new OC_Filestorage_DAV($this->config['webdav']);
|
||||
}
|
||||
|
||||
public function tearDown(){
|
||||
$this->instance->rmdir('/');
|
||||
public function tearDown(){
|
||||
$this->instance->rmdir('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,30 @@
|
|||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>album_index</name>
|
||||
<field>
|
||||
<name>album_id</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>album_name_index</name>
|
||||
<field>
|
||||
<name>album_name</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>album_artist_index</name>
|
||||
<field>
|
||||
<name>album_artist</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
|
||||
|
@ -81,6 +105,21 @@
|
|||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>artist_index</name>
|
||||
<field>
|
||||
<name>artist_id</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
<index>
|
||||
<name>artist_name_index</name>
|
||||
<field>
|
||||
<name>artist_name</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
|
||||
|
@ -223,6 +262,36 @@
|
|||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>song_index</name>
|
||||
<field>
|
||||
<name>song_id</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
<index>
|
||||
<name>song_album_index</name>
|
||||
<field>
|
||||
<name>song_album</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
<index>
|
||||
<name>song_artist_index</name>
|
||||
<field>
|
||||
<name>song_artist</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
<index>
|
||||
<name>song_name_index</name>
|
||||
<field>
|
||||
<name>song_name</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
|
||||
</declaration>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<id>media</id>
|
||||
<name>Media</name>
|
||||
<description>Media player and server for ownCloud</description>
|
||||
<version>0.2</version>
|
||||
<version>0.3</version>
|
||||
<licence>AGPL</licence>
|
||||
<author>Robin Appelman</author>
|
||||
<require>2</require>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +0,0 @@
|
|||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// dependencies.txt - part of getID3() //
|
||||
// See readme.txt for more details //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
lyrics3 depends on apetag (optional)
|
||||
ogg depends on flac
|
||||
id3v2 depends on id3v1
|
||||
apetag depends on id3v1 (optional, writing only)
|
||||
bonk depends on id3v2 (optional)
|
||||
riff depends on mp3
|
||||
mpeg depends on mp3
|
||||
quicktime depends on mp3
|
||||
flac depends on ogg
|
||||
optimfrog depends on riff
|
||||
la depends on riff
|
||||
lpac depends on riff
|
||||
asf depends on riff, id3v1 (optional)
|
|
@ -1,70 +0,0 @@
|
|||
<?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.audio.bink.php //
|
||||
// module for analyzing Bink or Smacker audio-video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class getid3_bink
|
||||
{
|
||||
|
||||
function getid3_bink(&$fd, &$ThisFileInfo) {
|
||||
|
||||
$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()';
|
||||
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
$fileTypeID = fread($fd, 3);
|
||||
switch ($fileTypeID) {
|
||||
case 'BIK':
|
||||
return $this->ParseBink($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
case 'SMK':
|
||||
return $this->ParseSmacker($fd, $ThisFileInfo);
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"';
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function ParseBink(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'bink';
|
||||
$ThisFileInfo['video']['dataformat'] = 'bink';
|
||||
|
||||
$fileData = 'BIK'.fread($fd, 13);
|
||||
|
||||
$ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
||||
$ThisFileInfo['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']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ParseSmacker(&$fd, &$ThisFileInfo) {
|
||||
$ThisFileInfo['fileformat'] = 'smacker';
|
||||
$ThisFileInfo['video']['dataformat'] = 'smacker';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,505 +0,0 @@
|
|||
<?php
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/// getID3() by James Heinrich <info@getid3.org> //
|
||||
// available at http://getid3.sourceforge.net //
|
||||
// or http://www.getid3.org //
|
||||
// //
|
||||
// FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
|
||||
// //
|
||||
// * version 0.1 (26 June 2005) //
|
||||
// //
|
||||
// minor modifications by James Heinrich <info@getid3.org> //
|
||||
// * version 0.1.1 (15 July 2005) //
|
||||
// //
|
||||
// Support for On2 VP6 codec and meta information //
|
||||
// by Steve Webster <steve.webster@featurecreep.com> //
|
||||
// * version 0.2 (22 February 2006) //
|
||||
// //
|
||||
// Modified to not read entire file into memory //
|
||||
// by James Heinrich <info@getid3.org> //
|
||||
// * version 0.3 (15 June 2006) //
|
||||
// //
|
||||
// Bugfixes for incorrectly parsed FLV dimensions //
|
||||
// and incorrect parsing of onMetaTag //
|
||||
// by Evgeny Moysevich <moysevich@gmail.com> //
|
||||
// * version 0.4 (07 December 2007) //
|
||||
// //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// module.audio-video.flv.php //
|
||||
// module for analyzing Shockwave Flash Video files //
|
||||
// dependencies: NONE //
|
||||
// ///
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
define('GETID3_FLV_TAG_AUDIO', 8);
|
||||
define('GETID3_FLV_TAG_VIDEO', 9);
|
||||
define('GETID3_FLV_TAG_META', 18);
|
||||
|
||||
define('GETID3_FLV_VIDEO_H263', 2);
|
||||
define('GETID3_FLV_VIDEO_SCREEN', 3);
|
||||
define('GETID3_FLV_VIDEO_VP6', 4);
|
||||
|
||||
class getid3_flv
|
||||
{
|
||||
|
||||
function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
|
||||
//$start_time = microtime(true);
|
||||
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
|
||||
|
||||
$FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
|
||||
$FLVheader = fread($fd, 5);
|
||||
|
||||
$ThisFileInfo['fileformat'] = 'flv';
|
||||
$ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3);
|
||||
$ThisFileInfo['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']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
|
||||
$ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
|
||||
|
||||
$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
|
||||
$FLVheaderFrameLength = 9;
|
||||
if ($FrameSizeDataLength > $FLVheaderFrameLength) {
|
||||
fseek($fd, $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;
|
||||
while ((ftell($fd) + 16) < $ThisFileInfo['avdataend']) {
|
||||
$ThisTagHeader = fread($fd, 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;
|
||||
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 & 0x07;
|
||||
$ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte & 0x30) / 0x10;
|
||||
$ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
|
||||
$ThisFileInfo['flv']['audio']['audioType'] = ($LastHeaderByte & 0x80) / 0x80;
|
||||
}
|
||||
break;
|
||||
|
||||
case GETID3_FLV_TAG_VIDEO:
|
||||
if (!$found_video) {
|
||||
$found_video = true;
|
||||
$ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
|
||||
|
||||
$FLVvideoHeader = fread($fd, 11);
|
||||
|
||||
if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) {
|
||||
|
||||
$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
|
||||
$PictureSizeType = $PictureSizeType & 0x0007;
|
||||
$ThisFileInfo['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;
|
||||
//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
|
||||
//$PictureSizeEnc <<= 1;
|
||||
//$ThisFileInfo['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;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
|
||||
$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;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$ThisFileInfo['video']['resolution_x'] = 352;
|
||||
$ThisFileInfo['video']['resolution_y'] = 288;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$ThisFileInfo['video']['resolution_x'] = 176;
|
||||
$ThisFileInfo['video']['resolution_y'] = 144;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
$ThisFileInfo['video']['resolution_x'] = 128;
|
||||
$ThisFileInfo['video']['resolution_y'] = 96;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$ThisFileInfo['video']['resolution_x'] = 320;
|
||||
$ThisFileInfo['video']['resolution_y'] = 240;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$ThisFileInfo['video']['resolution_x'] = 160;
|
||||
$ThisFileInfo['video']['resolution_y'] = 120;
|
||||
break;
|
||||
|
||||
default:
|
||||
$ThisFileInfo['video']['resolution_x'] = 0;
|
||||
$ThisFileInfo['video']['resolution_y'] = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Meta tag
|
||||
case GETID3_FLV_TAG_META:
|
||||
if (!$found_meta) {
|
||||
$found_meta = true;
|
||||
fseek($fd, -1, SEEK_CUR);
|
||||
$reader = new AMFReader(new AMFStream(fread($fd, $DataLength)));
|
||||
$eventName = $reader->readData();
|
||||
$ThisFileInfo['meta'][$eventName] = $reader->readData();
|
||||
unset($reader);
|
||||
|
||||
$ThisFileInfo['video']['frame_rate'] = @$ThisFileInfo['meta']['onMetaData']['framerate'];
|
||||
$ThisFileInfo['video']['resolution_x'] = @$ThisFileInfo['meta']['onMetaData']['width'];
|
||||
$ThisFileInfo['video']['resolution_y'] = @$ThisFileInfo['meta']['onMetaData']['height'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
|
||||
fseek($fd, $NextOffset, SEEK_SET);
|
||||
}
|
||||
|
||||
if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
|
||||
$ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['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']);
|
||||
|
||||
$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';
|
||||
}
|
||||
if (@$ThisFileInfo['flv']['header']['hasVideo']) {
|
||||
$ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
|
||||
$ThisFileInfo['video']['dataformat'] = 'flv';
|
||||
$ThisFileInfo['video']['lossless'] = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function FLVaudioFormat($id) {
|
||||
$FLVaudioFormat = array(
|
||||
0 => 'uncompressed',
|
||||
1 => 'ADPCM',
|
||||
2 => 'mp3',
|
||||
5 => 'Nellymoser 8kHz mono',
|
||||
6 => 'Nellymoser',
|
||||
);
|
||||
return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioRate($id) {
|
||||
$FLVaudioRate = array(
|
||||
0 => 5500,
|
||||
1 => 11025,
|
||||
2 => 22050,
|
||||
3 => 44100,
|
||||
);
|
||||
return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
|
||||
}
|
||||
|
||||
function FLVaudioBitDepth($id) {
|
||||
$FLVaudioBitDepth = array(
|
||||
0 => 8,
|
||||
1 => 16,
|
||||
);
|
||||
return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
|
||||
}
|
||||
|
||||
function FLVvideoCodec($id) {
|
||||
$FLVvideoCodec = array(
|
||||
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
|
||||
GETID3_FLV_VIDEO_SCREEN => 'Screen video',
|
||||
GETID3_FLV_VIDEO_VP6 => 'On2 VP6',
|
||||
);
|
||||
return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
|
||||
}
|
||||
}
|
||||
|
||||
class AMFStream {
|
||||
var $bytes;
|
||||
var $pos;
|
||||
|
||||
function AMFStream(&$bytes) {
|
||||
$this->bytes =& $bytes;
|
||||
$this->pos = 0;
|
||||
}
|
||||
|
||||
function readByte() {
|
||||
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
|
||||
}
|
||||
|
||||
function readInt() {
|
||||
return ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readLong() {
|
||||
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return getid3_lib::BigEndian2Float($this->read(8));
|
||||
}
|
||||
|
||||
function readUTF() {
|
||||
$length = $this->readInt();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function readLongUTF() {
|
||||
$length = $this->readLong();
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
function read($length) {
|
||||
$val = substr($this->bytes, $this->pos, $length);
|
||||
$this->pos += $length;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekByte() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readByte();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekInt() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readInt();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLong() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLong();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekDouble() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readDouble();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function peekLongUTF() {
|
||||
$pos = $this->pos;
|
||||
$val = $this->readLongUTF();
|
||||
$this->pos = $pos;
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
class AMFReader {
|
||||
var $stream;
|
||||
|
||||
function AMFReader(&$stream) {
|
||||
$this->stream =& $stream;
|
||||
}
|
||||
|
||||
function readData() {
|
||||
$value = null;
|
||||
|
||||
$type = $this->stream->readByte();
|
||||
switch ($type) {
|
||||
|
||||
// Double
|
||||
case 0:
|
||||
$value = $this->readDouble();
|
||||
break;
|
||||
|
||||
// Boolean
|
||||
case 1:
|
||||
$value = $this->readBoolean();
|
||||
break;
|
||||
|
||||
// String
|
||||
case 2:
|
||||
$value = $this->readString();
|
||||
break;
|
||||
|
||||
// Object
|
||||
case 3:
|
||||
$value = $this->readObject();
|
||||
break;
|
||||
|
||||
// null
|
||||
case 6:
|
||||
return null;
|
||||
break;
|
||||
|
||||
// Mixed array
|
||||
case 8:
|
||||
$value = $this->readMixedArray();
|
||||
break;
|
||||
|
||||
// Array
|
||||
case 10:
|
||||
$value = $this->readArray();
|
||||
break;
|
||||
|
||||
// Date
|
||||
case 11:
|
||||
$value = $this->readDate();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
case 13:
|
||||
$value = $this->readLongString();
|
||||
break;
|
||||
|
||||
// XML (handled as string)
|
||||
case 15:
|
||||
$value = $this->readXML();
|
||||
break;
|
||||
|
||||
// Typed object (handled as object)
|
||||
case 16:
|
||||
$value = $this->readTypedObject();
|
||||
break;
|
||||
|
||||
// Long string
|
||||
default:
|
||||
$value = '(unknown or unsupported data type)';
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function readDouble() {
|
||||
return $this->stream->readDouble();
|
||||
}
|
||||
|
||||
function readBoolean() {
|
||||
return $this->stream->readByte() == 1;
|
||||
}
|
||||
|
||||
function readString() {
|
||||
return $this->stream->readUTF();
|
||||
}
|
||||
|
||||
function readObject() {
|
||||
// Get highest numerical index - ignored
|
||||
// $highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readMixedArray() {
|
||||
// Get highest numerical index - ignored
|
||||
$highestIndex = $this->stream->readLong();
|
||||
|
||||
$data = array();
|
||||
|
||||
while ($key = $this->stream->readUTF()) {
|
||||
if (is_numeric($key)) {
|
||||
$key = (float) $key;
|
||||
}
|
||||
$data[$key] = $this->readData();
|
||||
}
|
||||
// Mixed array record ends with empty string (0x00 0x00) and 0x09
|
||||
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
|
||||
// Consume byte
|
||||
$this->stream->readByte();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readArray() {
|
||||
$length = $this->stream->readLong();
|
||||
$data = array();
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$data[] = $this->readData();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function readDate() {
|
||||
$timestamp = $this->stream->readDouble();
|
||||
$timezone = $this->stream->readInt();
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
function readLongString() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readXML() {
|
||||
return $this->stream->readLongUTF();
|
||||
}
|
||||
|
||||
function readTypedObject() {
|
||||
$className = $this->stream->readUTF();
|
||||
return $this->readObject();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue