initial commit of starter install drupal 7

This commit is contained in:
Bachir Soussi Chiadmi
2015-02-21 17:21:40 +01:00
commit eeba51cea6
6113 changed files with 1249218 additions and 0 deletions
+222
View File
@@ -0,0 +1,222 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// //
// extension.cache.dbm.php - part of getID3() //
// Please see readme.txt for more information //
// ///
/////////////////////////////////////////////////////////////////
// //
// This extension written by Allan Hansen <ahØartemis*dk> //
// ///
/////////////////////////////////////////////////////////////////
/**
* This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information very fast
*
* Example:
*
* Normal getID3 usage (example):
*
* require_once 'getid3/getid3.php';
* $getID3 = new getID3;
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
* getID3_cached usage:
*
* require_once 'getid3/getid3.php';
* require_once 'getid3/getid3/extension.cache.dbm.php';
* $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
* '/tmp/getid3_cache.lock');
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
*
* Supported Cache Types
*
* SQL Databases: (use extension.cache.mysql)
*
* cache_type cache_options
* -------------------------------------------------------------------
* mysql host, database, username, password
*
*
* DBM-Style Databases: (this extension)
*
* cache_type cache_options
* -------------------------------------------------------------------
* gdbm dbm_filename, lock_filename
* ndbm dbm_filename, lock_filename
* db2 dbm_filename, lock_filename
* db3 dbm_filename, lock_filename
* db4 dbm_filename, lock_filename (PHP5 required)
*
* PHP must have write access to both dbm_filename and lock_filename.
*
*
* Recommended Cache Types
*
* Infrequent updates, many reads any DBM
* Frequent updates mysql
*/
class getID3_cached_dbm extends getID3
{
// public: constructor - see top of this file for cache type and cache_options
function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
// Check for dba extension
if (!extension_loaded('dba')) {
die('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.');
}
}
// Create lock file if needed
if (!file_exists($lock_filename)) {
if (!touch($lock_filename)) {
die('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');
}
$this->lock = fopen($lock_filename, 'w');
// Acquire exclusive write lock to lock file
flock($this->lock, LOCK_EX);
// Create dbm-file if needed
if (!file_exists($dbm_filename)) {
if (!touch($dbm_filename)) {
die('failed to create dbm file: ' . $dbm_filename);
}
}
// Try to open dbm file for writing
$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);
}
// Insert getID3 version number
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
}
// Init misc values
$this->cache_type = $cache_type;
$this->dbm_filename = $dbm_filename;
// Register destructor
register_shutdown_function(array($this, '__destruct'));
// Check version number and clear cache if changed
if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) {
$this->clear_cache();
}
parent::getID3();
}
// public: destuctor
function __destruct() {
// Close dbm file
@dba_close($this->dba);
// Release exclusive lock
@flock($this->lock, LOCK_UN);
// Close lock file
@fclose($this->lock);
}
// public: clear cache
function clear_cache() {
// Close dbm file
dba_close($this->dba);
// Create new dbm file
$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);
}
// Insert getID3 version number
dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
// Reregister shutdown function
register_shutdown_function(array($this, '__destruct'));
}
// public: analyze file
function analyze($filename) {
if (file_exists($filename)) {
// Calc key filename::mod_time::size - should be unique
$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename);
// Loopup key
$result = dba_fetch($key, $this->dba);
// Hit
if ($result !== false) {
return unserialize($result);
}
}
// Miss
$result = parent::analyze($filename);
// Save result
if (file_exists($filename)) {
dba_insert($key, serialize($result), $this->dba);
}
return $result;
}
}
?>
+171
View File
@@ -0,0 +1,171 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// //
// extension.cache.mysql.php - part of getID3() //
// Please see readme.txt for more information //
// ///
/////////////////////////////////////////////////////////////////
// //
// This extension written by Allan Hansen <ahØartemis*dk> //
// ///
/////////////////////////////////////////////////////////////////
/**
* This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information very fast
*
* Example: (see also demo.cache.mysql.php in /demo/)
*
* Normal getID3 usage (example):
*
* require_once 'getid3/getid3.php';
* $getID3 = new getID3;
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
* getID3_cached usage:
*
* require_once 'getid3/getid3.php';
* require_once 'getid3/getid3/extension.cache.mysql.php';
* $getID3 = new getID3_cached_mysql('localhost', 'database',
* 'username', 'password');
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
*
* Supported Cache Types (this extension)
*
* SQL Databases:
*
* cache_type cache_options
* -------------------------------------------------------------------
* mysql host, database, username, password
*
*
* DBM-Style Databases: (use extension.cache.dbm)
*
* cache_type cache_options
* -------------------------------------------------------------------
* gdbm dbm_filename, lock_filename
* ndbm dbm_filename, lock_filename
* db2 dbm_filename, lock_filename
* db3 dbm_filename, lock_filename
* db4 dbm_filename, lock_filename (PHP5 required)
*
* PHP must have write access to both dbm_filename and lock_filename.
*
*
* Recommended Cache Types
*
* Infrequent updates, many reads any DBM
* Frequent updates mysql
*/
class getID3_cached_mysql extends getID3
{
// private vars
var $cursor;
var $connection;
// public: constructor - see top of this file for cache type and cache_options
function getID3_cached_mysql($host, $database, $username, $password) {
// Check for mysql support
if (!function_exists('mysql_pconnect')) {
die('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.');
}
// Select database
if (!mysql_select_db($database, $this->connection)) {
die('Cannot use database '.$database);
}
// 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) {
$this->clear_cache();
}
parent::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);
}
// public: analyze file
function analyze($filename) {
if (file_exists($filename)) {
// 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);
}
}
// Miss
$result = 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);
}
return $result;
}
// private: (re)create sql table
function create_table($drop = false) {
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
`filename` VARCHAR(255) NOT NULL DEFAULT '',
`filesize` INT(11) NOT NULL DEFAULT '0',
`filetime` INT(11) NOT NULL DEFAULT '0',
`analyzetime` INT(11) NOT NULL DEFAULT '0',
`value` TEXT NOT NULL,
PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection);
echo mysql_error($this->connection);
}
}
?>
File diff suppressed because it is too large Load Diff
+1397
View File
File diff suppressed because it is too large Load Diff
+271
View File
@@ -0,0 +1,271 @@
<?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.gzip.php //
// module for analyzing GZIP files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
// //
// Module originally written by //
// Mike Mozolin <teddybearØmail*ru> //
// //
/////////////////////////////////////////////////////////////////
class getid3_gzip {
// 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';
$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']);
$arr_members = explode("\x1F\x8B\x08", $buffer);
while (true) {
$is_wrong_members = false;
$num_members = intval(count($arr_members));
for ($i = 0; $i < $num_members; $i++) {
if (strlen($arr_members[$i]) == 0) {
continue;
}
$buf = "\x1F\x8B\x08".$arr_members[$i];
$attr = unpack($unpack_header, substr($buf, 0, $start_length));
if (!$this->get_os_type(ord($attr['os']))) {
// Merge member with previous if wrong OS type
$arr_members[$i - 1] .= $buf;
$arr_members[$i] = '';
$is_wrong_members = true;
continue;
}
}
if (!$is_wrong_members) {
break;
}
}
$ThisFileInfo['gzip']['files'] = array();
$fpointer = 0;
$idx = 0;
for ($i = 0; $i < $num_members; $i++) {
if (strlen($arr_members[$i]) == 0) {
continue;
}
$thisThisFileInfo = &$ThisFileInfo['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']);
$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);
$thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']);
$thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']);
if (!$thisThisFileInfo['os']) {
$ThisFileInfo['error'][] = 'Read error on gzip file';
return false;
}
$fpointer = 10;
$arr_xsubfield = array();
// bit 2 - FLG.FEXTRA
//+---+---+=================================+
//| XLEN |...XLEN bytes of "extra field"...|
//+---+---+=================================+
if ($thisThisFileInfo['flags']['extra']) {
$w_xlen = substr($buff, $fpointer, 2);
$xlen = getid3_lib::LittleEndian2Int($w_xlen);
$fpointer += 2;
$thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
// Extra SubFields
//+---+---+---+---+==================================+
//|SI1|SI2| LEN |... LEN bytes of subfield data ...|
//+---+---+---+---+==================================+
$idx = 0;
while (true) {
if ($idx >= $xlen) {
break;
}
$si1 = ord(substr($buff, $fpointer + $idx++, 1));
$si2 = ord(substr($buff, $fpointer + $idx++, 1));
if (($si1 == 0x41) && ($si2 == 0x70)) {
$w_xsublen = substr($buff, $fpointer + $idx, 2);
$xsublen = getid3_lib::LittleEndian2Int($w_xsublen);
$idx += 2;
$arr_xsubfield[] = substr($buff, $fpointer + $idx, $xsublen);
$idx += $xsublen;
} else {
break;
}
}
$fpointer += $xlen;
}
// bit 3 - FLG.FNAME
//+=========================================+
//|...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']) {
while (true) {
if (ord($buff[$fpointer]) == 0) {
$fpointer++;
break;
}
$thisThisFileInfo['filename'] .= $buff[$fpointer];
$fpointer++;
}
}
// bit 4 - FLG.FCOMMENT
//+===================================+
//|...file comment, zero-terminated...|
//+===================================+
if ($thisThisFileInfo['flags']['comment']) {
while (true) {
if (ord($buff[$fpointer]) == 0) {
$fpointer++;
break;
}
$thisThisFileInfo['comment'] .= $buff[$fpointer];
$fpointer++;
}
}
// bit 1 - FLG.FHCRC
//+---+---+
//| CRC16 |
//+---+---+
if ($thisThisFileInfo['flags']['crc16']) {
$w_crc = substr($buff, $fpointer, 2);
$thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
$fpointer += 2;
}
// bit 0 - FLG.FTEXT
//if ($thisThisFileInfo['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));
$ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize']));
if ($this->option_gzip_parse_contents) {
// Try to inflate GZip
$csize = 0;
$inflated = '';
$chkcrc32 = '';
if (function_exists('gzinflate')) {
$cdata = substr($buff, $fpointer);
$cdata = substr($cdata, 0, strlen($cdata) - 8);
$csize = strlen($cdata);
$inflated = gzinflate($cdata);
// Calculate CRC32 for inflated content
$thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']);
// determine format
$formattest = substr($inflated, 0, 32774);
$newgetID3 = new getID3();
$determined_format = $newgetID3->GetFileFormat($formattest);
unset($newgetID3);
// file format is determined
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) {
// can't find anywhere to create a temp file, abort
$ThisFileInfo['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);
unlink($temp_tar_filename);
} else {
$ThisFileInfo['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file';
break;
}
}
break;
case '':
default:
// unknown or unhandled format
break;
}
}
}
}
return true;
}
// Converts the OS type
function get_os_type($key) {
static $os_type = array(
'0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
'1' => 'Amiga',
'2' => 'VMS (or OpenVMS)',
'3' => 'Unix',
'4' => 'VM/CMS',
'5' => 'Atari TOS',
'6' => 'HPFS filesystem (OS/2, NT)',
'7' => 'Macintosh',
'8' => 'Z-System',
'9' => 'CP/M',
'10' => 'TOPS-20',
'11' => 'NTFS filesystem (NT)',
'12' => 'QDOS',
'13' => 'Acorn RISCOS',
'255' => 'unknown'
);
return @$os_type[$key];
}
// Converts the eXtra FLags
function get_xflag_type($key) {
static $xflag_type = array(
'0' => 'unknown',
'2' => 'maximum compression',
'4' => 'fastest algorithm'
);
return @$xflag_type[$key];
}
}
?>
+52
View File
@@ -0,0 +1,52 @@
<?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.rar.php //
// module for analyzing RAR files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_rar
{
var $option_use_rar_extension = false;
function getid3_rar(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'rar';
if ($this->option_use_rar_extension === true) {
if (function_exists('rar_open')) {
if ($rp = rar_open($ThisFileInfo['filename'])) {
$ThisFileInfo['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()));
}
rar_close($rp);
return true;
} else {
$ThisFileInfo['error'][] = 'failed to rar_open('.$ThisFileInfo['filename'].')';
}
} else {
$ThisFileInfo['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)';
}
return false;
}
}
?>
+97
View File
@@ -0,0 +1,97 @@
<?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.szip.php //
// module for analyzing SZIP compressed files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_szip
{
function getid3_szip(&$fd, &$ThisFileInfo) {
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).'"';
return false;
}
$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);
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);
break;
case 'BH':
$BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3));
$BHheaderdata = fread($fd, $BHheaderbytes);
$BHheaderoffset = 0;
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
//filename as \0 terminated string (empty string indicates end)
//owner as \0 terminated string (empty is same as last file)
//group as \0 terminated string (empty is same as last file)
//3 byte filelength in this block
//2 byte access flags
//4 byte creation time (like in unix)
//4 byte modification time (like in unix)
//4 byte access time (like in unix)
$BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
$BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
$BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$BHheaderoffset += (strlen($BHdataArray['group']) + 1);
$BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
$BHheaderoffset += 3;
$BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
$BHheaderoffset += 2;
$BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$BHheaderoffset += 4;
$BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$BHheaderoffset += 4;
$BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$BHheaderoffset += 4;
$ThisFileInfo['szip']['BH'][] = $BHdataArray;
}
break;
default:
break 2;
}
}
return true;
}
}
?>
+175
View File
@@ -0,0 +1,175 @@
<?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.tar.php //
// module for analyzing TAR files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
// //
// Module originally written by //
// Mike Mozolin <teddybearØmail*ru> //
// //
/////////////////////////////////////////////////////////////////
class getid3_tar {
function getid3_tar(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'tar';
$ThisFileInfo['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);
if (strlen($buffer) < 512) {
break;
}
// check the block
$checksum = 0;
for ($i = 0; $i < 148; $i++) {
$checksum += ord($buffer{$i});
}
for ($i = 148; $i < 156; $i++) {
$checksum += ord(' ');
}
for ($i = 156; $i < 512; $i++) {
$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']);
if (($checksum == 256) && ($chksum == 0)) {
// EOF Found
break;
}
if ($prefix) {
$name = $prefix.'/'.$name;
}
if ((preg_match('#/$#', $name)) && !$name) {
$typeflag = 5;
}
if ($buffer == $null_512k) {
// it's the end of the tar-file...
break;
}
// Read to the next chunk
fseek($fd, $size, SEEK_CUR);
$diff = $size % 512;
if ($diff != 0) {
// Padding, throw away
fseek($fd, (512 - $diff), SEEK_CUR);
}
// Protect against tar-files with garbage at the end
if ($name == '') {
break;
}
$ThisFileInfo['tar']['file_details'][$name] = array (
'name' => $name,
'mode_raw' => $mode,
'mode' => getid3_tar::display_perms($mode),
'uid' => $uid,
'gid' => $gid,
'size' => $size,
'mtime' => $mtime,
'chksum' => $chksum,
'typeflag' => getid3_tar::get_flag_type($typflag),
'linkname' => $lnkname,
'magic' => $magic,
'version' => $ver,
'uname' => $uname,
'gname' => $gname,
'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));
}
return true;
}
// Parses the file mode to file permissions
function display_perms($mode) {
// Determine Type
if ($mode & 0x1000) $type='p'; // FIFO pipe
elseif ($mode & 0x2000) $type='c'; // Character special
elseif ($mode & 0x4000) $type='d'; // Directory
elseif ($mode & 0x6000) $type='b'; // Block special
elseif ($mode & 0x8000) $type='-'; // Regular
elseif ($mode & 0xA000) $type='l'; // Symbolic Link
elseif ($mode & 0xC000) $type='s'; // Socket
else $type='u'; // UNKNOWN
// Determine permissions
$owner['read'] = (($mode & 00400) ? 'r' : '-');
$owner['write'] = (($mode & 00200) ? 'w' : '-');
$owner['execute'] = (($mode & 00100) ? 'x' : '-');
$group['read'] = (($mode & 00040) ? 'r' : '-');
$group['write'] = (($mode & 00020) ? 'w' : '-');
$group['execute'] = (($mode & 00010) ? 'x' : '-');
$world['read'] = (($mode & 00004) ? 'r' : '-');
$world['write'] = (($mode & 00002) ? 'w' : '-');
$world['execute'] = (($mode & 00001) ? 'x' : '-');
// Adjust for SUID, SGID and sticky bit
if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S';
if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T';
$s = sprintf('%1s', $type);
$s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']);
$s .= sprintf('%1s%1s%1s', $group['read'], $group['write'], $group['execute']);
$s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']);
return $s;
}
// Converts the file type
function get_flag_type($typflag) {
static $flag_types = array(
'0' => 'LF_NORMAL',
'1' => 'LF_LINK',
'2' => 'LF_SYNLINK',
'3' => 'LF_CHR',
'4' => 'LF_BLK',
'5' => 'LF_DIR',
'6' => 'LF_FIFO',
'7' => 'LF_CONFIG',
'D' => 'LF_DUMPDIR',
'K' => 'LF_LONGLINK',
'L' => 'LF_LONGNAME',
'M' => 'LF_MULTIVOL',
'N' => 'LF_NAMES',
'S' => 'LF_SPARSE',
'V' => 'LF_VOLHDR'
);
return @$flag_types[$typflag];
}
}
?>
+416
View File
@@ -0,0 +1,416 @@
<?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.zip.php //
// module for analyzing pkZip files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_zip
{
function getid3_zip(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'zip';
$ThisFileInfo['zip']['encoding'] = 'ISO-8859-1';
$ThisFileInfo['zip']['files'] = array();
$ThisFileInfo['zip']['compressed_size'] = 0;
$ThisFileInfo['zip']['uncompressed_size'] = 0;
$ThisFileInfo['zip']['entries_count'] = 0;
if ($ThisFileInfo['filesize'] < pow(2, 31)) {
$EOCDsearchData = '';
$EOCDsearchCounter = 0;
while ($EOCDsearchCounter++ < 512) {
fseek($fd, -128 * $EOCDsearchCounter, SEEK_END);
$EOCDsearchData = fread($fd, 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($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'];
if ($centraldirectoryentry['uncompressed_size'] > 0) {
$ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['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?)';
return false;
}
if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
$ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['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($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) {
$ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['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';
}
return true;
}
}
}
if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) {
// 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)';
}
$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'];
}
return true;
} else {
unset($ThisFileInfo['zip']);
$ThisFileInfo['fileformat'] = '';
$ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
return false;
}
}
function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'zip';
$ThisFileInfo['zip']['compressed_size'] = 0;
$ThisFileInfo['zip']['uncompressed_size'] = 0;
$ThisFileInfo['zip']['entries_count'] = 0;
rewind($fd);
while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
$ThisFileInfo['zip']['entries'][] = $fileentry;
$ThisFileInfo['zip']['entries_count']++;
}
if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['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'];
}
if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
return false;
}
if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) {
$ThisFileInfo['zip']['end_central_directory'] = $EOCD;
} else {
$ThisFileInfo['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'];
}
return true;
}
function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) {
$ThisFileInfo['zip']['compressed_size'] = 0;
$ThisFileInfo['zip']['uncompressed_size'] = 0;
$ThisFileInfo['zip']['entries_count'] = 0;
rewind($fd);
while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
$ThisFileInfo['zip']['entries'][] = $fileentry;
$ThisFileInfo['zip']['entries_count']++;
$ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size'];
$ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
}
if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['error'][] = 'No Local File Header entries found';
return false;
}
return true;
}
function ZIPparseLocalFileHeader(&$fd) {
$LocalFileHeader['offset'] = ftell($fd);
$ZIPlocalFileHeader = fread($fd, 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
return false;
}
$LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
$LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2));
$LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2));
$LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
$LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
$LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
$LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
$LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
$LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
$LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
$LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10);
$LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8);
$LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']);
$LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size'];
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size'];
$LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
$LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
if ($FilenameExtrafieldLength > 0) {
$ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength);
if ($LocalFileHeader['raw']['filename_length'] > 0) {
$LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
}
if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
$LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
}
}
$LocalFileHeader['data_offset'] = ftell($fd);
//$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']);
fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR);
if ($LocalFileHeader['flags']['data_descriptor_used']) {
$DataDescriptor = fread($fd, 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));
}
return $LocalFileHeader;
}
function ZIPparseCentralDirectory(&$fd) {
$CentralDirectory['offset'] = ftell($fd);
$ZIPcentralDirectory = fread($fd, 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
return false;
}
$CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
$CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2));
$CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2));
$CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
$CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
$CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
$CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
$CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
$CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
$CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
$CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
$CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
$CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2));
$CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2));
$CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4));
$CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
$CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset'];
$CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
$CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
$CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
$CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
$CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size'];
$CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size'];
$CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
$CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
if ($FilenameExtrafieldCommentLength > 0) {
$FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength);
if ($CentralDirectory['raw']['filename_length'] > 0) {
$CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
}
if ($CentralDirectory['raw']['extra_field_length'] > 0) {
$CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']);
}
if ($CentralDirectory['raw']['file_comment_length'] > 0) {
$CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
}
}
return $CentralDirectory;
}
function ZIPparseEndOfCentralDirectory(&$fd) {
$EndOfCentralDirectory['offset'] = ftell($fd);
$ZIPendOfCentralDirectory = fread($fd, 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
return false;
}
$EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
$EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2));
$EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2));
$EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
$EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
$EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
$EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
if ($EndOfCentralDirectory['comment_length'] > 0) {
$EndOfCentralDirectory['comment'] = fread($fd, $EndOfCentralDirectory['comment_length']);
}
return $EndOfCentralDirectory;
}
function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
switch ($compressionmethod) {
case 6:
$ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096);
$ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2);
break;
case 8:
case 9:
switch (($flagbytes & 0x0006) >> 1) {
case 0:
$ParsedFlags['compression_speed'] = 'normal';
break;
case 1:
$ParsedFlags['compression_speed'] = 'maximum';
break;
case 2:
$ParsedFlags['compression_speed'] = 'fast';
break;
case 3:
$ParsedFlags['compression_speed'] = 'superfast';
break;
}
break;
}
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
return $ParsedFlags;
}
function ZIPversionOSLookup($index) {
static $ZIPversionOSLookup = array(
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
1 => 'Amiga',
2 => 'OpenVMS',
3 => 'Unix',
4 => 'VM/CMS',
5 => 'Atari ST',
6 => 'OS/2 H.P.F.S.',
7 => 'Macintosh',
8 => 'Z-System',
9 => 'CP/M',
10 => 'Windows NTFS',
11 => 'MVS',
12 => 'VSE',
13 => 'Acorn Risc',
14 => 'VFAT',
15 => 'Alternate MVS',
16 => 'BeOS',
17 => 'Tandem'
);
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
}
function ZIPcompressionMethodLookup($index) {
static $ZIPcompressionMethodLookup = array(
0 => 'store',
1 => 'shrink',
2 => 'reduce-1',
3 => 'reduce-2',
4 => 'reduce-3',
5 => 'reduce-4',
6 => 'implode',
7 => 'tokenize',
8 => 'deflate',
9 => 'deflate64',
10 => 'PKWARE Date Compression Library Imploding'
);
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
}
function DOStime2UNIXtime($DOSdate, $DOStime) {
// wFatDate
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
// Bits Contents
// 0-4 Day of the month (1-31)
// 5-8 Month (1 = January, 2 = February, and so on)
// 9-15 Year offset from 1980 (add 1980 to get actual year)
$UNIXday = ($DOSdate & 0x001F);
$UNIXmonth = (($DOSdate & 0x01E0) >> 5);
$UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980;
// wFatTime
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
// Bits Contents
// 0-4 Second divided by 2
// 5-10 Minute (0-59)
// 11-15 Hour (0-23 on a 24-hour clock)
$UNIXsecond = ($DOStime & 0x001F) * 2;
$UNIXminute = (($DOStime & 0x07E0) >> 5);
$UNIXhour = (($DOStime & 0xF800) >> 11);
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
}
}
?>
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
<?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;
}
}
?>
+505
View File
@@ -0,0 +1,505 @@
<?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();
}
}
?>
File diff suppressed because it is too large Load Diff
+292
View File
@@ -0,0 +1,292 @@
<?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-video.mpeg.php //
// module for analyzing MPEG files //
// dependencies: module.audio.mp3.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4");
define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7");
define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8");
define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0");
class getid3_mpeg
{
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'].')';
return false;
}
$ThisFileInfo['fileformat'] = 'mpeg';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']));
$MPEGstreamDataLength = strlen($MPEGstreamData);
$foundVideo = true;
$VideoChunkOffset = 0;
while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
if ($VideoChunkOffset >= $MPEGstreamDataLength) {
$foundVideo = false;
break;
}
}
if ($foundVideo) {
// Start code 32 bits
// horizontal frame size 12 bits
// vertical frame size 12 bits
// pixel aspect ratio 4 bits
// frame rate 4 bits
// bitrate 18 bits
// marker bit 1 bit
// VBV buffer size 10 bits
// constrained parameter flag 1 bit
// intra quant. matrix flag 1 bit
// intra quant. matrix values 512 bits (present if matrix flag == 1)
// non-intra quant. matrix flag 1 bit
// non-intra quant. matrix values 512 bits (present if matrix flag == 1)
$ThisFileInfo['video']['dataformat'] = 'mpeg';
$VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
$FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
$VideoChunkOffset += 3;
$AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
$VideoChunkOffset += 1;
$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);
$ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'];
$ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['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']);
$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']) {
// read 512 bits
$ThisFileInfo['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);
if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
$ThisFileInfo['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);
$VideoChunkOffset += 64;
}
}
if ($ThisFileInfo['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';
} else {
$ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400;
$ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr';
$ThisFileInfo['video']['bitrate'] = $ThisFileInfo['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;
} 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?';
}
//0x000001B3 begins the sequence_header of every MPEG video stream.
//But in MPEG-2, this header must immediately be followed by an
//extension_start_code (0x000001B5) with a sequence_extension ID (1).
//(This extension contains all the additional MPEG-2 stuff.)
//MPEG-1 doesn't have this extension, so that's a sure way to tell the
//difference between MPEG-1 and MPEG-2 video streams.
if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
$ThisFileInfo['video']['codec'] = 'MPEG-2';
} else {
$ThisFileInfo['video']['codec'] = 'MPEG-1';
}
$AudioChunkOffset = 0;
while (true) {
while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
if ($AudioChunkOffset >= $MPEGstreamDataLength) {
break 2;
}
}
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;
break 2;
}
}
}
// 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']);
// 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']);
//switch ($ThisFileInfo['video']['bitrate']) {
// case('5000000'):
// $multiplier = 0.93292642112380355828048824319889;
// break;
// case('5500000'):
// $multiplier = 0.93582895375200989965359777343219;
// break;
// case('6000000'):
// $multiplier = 0.93796247714820932532911373859139;
// break;
// case('7000000'):
// $multiplier = 0.9413264083635103463010117778776;
// break;
// default:
// $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.';
}
}
return true;
}
function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
$OverheadPercentage = 0;
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
$OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
$OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
$OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
$OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
$BitrateToUseMin = 32;
$BitrateToUseMax = 32;
$previousBitrate = 32;
foreach ($OverheadMultiplierByBitrate as $key => $value) {
if ($AudioBitrate >= $previousBitrate) {
$BitrateToUseMin = $previousBitrate;
}
if ($AudioBitrate < $key) {
$BitrateToUseMax = $key;
break;
}
$previousBitrate = $key;
}
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
$VideoBitrateLog10 = log10($VideoBitrate);
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
return $OverheadPercentage;
}
function MPEGvideoFramerateLookup($rawframerate) {
$MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
}
function MPEGvideoAspectRatioLookup($rawaspectratio) {
$MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
}
function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
$MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
}
}
?>
+224
View File
@@ -0,0 +1,224 @@
<?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
{
function getid3_nsv(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$NSVheader = fread($fd, 4);
switch ($NSVheader) {
case 'NSVs':
if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) {
$ThisFileInfo['fileformat'] = 'nsv';
$ThisFileInfo['audio']['dataformat'] = 'nsv';
$ThisFileInfo['video']['dataformat'] = 'nsv';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['video']['lossless'] = false;
}
break;
case 'NSVf':
if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) {
$ThisFileInfo['fileformat'] = 'nsv';
$ThisFileInfo['audio']['dataformat'] = 'nsv';
$ThisFileInfo['video']['dataformat'] = 'nsv';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['video']['lossless'] = false;
$this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']);
}
break;
default:
$ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"';
return false;
break;
}
if (!isset($ThisFileInfo['nsv']['NSVf'])) {
$ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
}
return true;
}
function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) {
fseek($fd, $fileoffset, SEEK_SET);
$NSVsheader = fread($fd, 28);
$offset = 0;
$ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
$offset += 4;
if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') {
$ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead';
unset($ThisFileInfo['nsv']['NSVs']);
return false;
}
$ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
$ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
$offset += 4;
$ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
$offset += 4;
$ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
$offset += 2;
$ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
$offset += 2;
$ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
//$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) {
case 'PCM ':
$ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
$ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
$offset += 1;
$ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
$offset += 2;
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate'];
break;
case 'MP3 ':
case 'NONE':
default:
//$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
$offset += 4;
break;
}
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x'];
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y'];
$ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']);
$ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate'];
$ThisFileInfo['video']['bits_per_sample'] = 24;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
return true;
}
function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) {
fseek($fd, $fileoffset, SEEK_SET);
$NSVfheader = fread($fd, 28);
$offset = 0;
$ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
$offset += 4;
if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') {
$ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead';
unset($ThisFileInfo['nsv']['NSVf']);
return false;
}
$ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset;
$ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
$ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) {
$ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes';
}
$ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
$ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
$ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
$ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
return false;
}
$NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2']));
$NSVfheaderlength = strlen($NSVfheader);
$ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']);
$offset += $ThisFileInfo['nsv']['NSVf']['meta_size'];
if ($getTOCoffsets) {
$TOCcounter = 0;
while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
$ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
$offset += 4;
$TOCcounter++;
}
}
}
if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') {
$ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']);
$CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']);
foreach ($CommentPairArray as $CommentPair) {
if (strstr($CommentPair, '='."\x01")) {
list($key, $value) = explode('='."\x01", $CommentPair, 2);
$ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
}
}
}
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000;
$ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds'];
return true;
}
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
+528
View File
@@ -0,0 +1,528 @@
<?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-video.real.php //
// module for analyzing Real Audio/Video files //
// dependencies: module.audio-video.riff.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_real
{
function getid3_real(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'real';
$ThisFileInfo['bitrate'] = 0;
$ThisFileInfo['playtime_seconds'] = 0;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$ChunkCounter = 0;
while (ftell($fd) < $ThisFileInfo['avdataend']) {
$ChunkData = fread($fd, 8);
$ChunkName = substr($ChunkData, 0, 4);
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
if ($ChunkName == '.ra'."\xFD") {
$ChunkData .= fread($fd, $ChunkSize - 8);
if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) {
$ThisFileInfo['audio']['dataformat'] = 'real';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['real']['old_ra_header']['sample_rate'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['real']['old_ra_header']['channels'];
$ThisFileInfo['playtime_seconds'] = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']);
$ThisFileInfo['audio']['bitrate'] = 8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']);
$ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']);
foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) {
if (strlen(trim($valuearray[0])) > 0) {
$ThisFileInfo['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']);
return false;
}
// shortcut
$ThisFileInfo['real']['chunks'][$ChunkCounter] = array();
$thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter];
$thisfile_real_chunks_currentchunk['name'] = $ChunkName;
$thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 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';
return false;
}
if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) {
$ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8);
fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET);
} elseif(($ChunkSize - 8) > 0) {
$ChunkData .= fread($fd, $ChunkSize - 8);
}
$offset = 8;
switch ($ChunkName) {
case '.RMF': // RealMedia File Header
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
switch ($thisfile_real_chunks_currentchunk['object_version']) {
case 0:
$thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
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)';
break;
}
break;
case 'PROP': // Properties Header
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$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;
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
$ThisFileInfo['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);
$thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
}
break;
case 'MDPR': // Media Properties Header
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
$offset += 1;
$thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
$offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
$thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
$offset += 1;
$thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
$offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
$thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
$offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
// shortcut
$thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
case 'video/x-pn-realvideo':
case 'video/x-pn-multirate-realvideo':
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
// shortcut
$thisfile_real_chunks_currentchunk['video_info'] = array();
$thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
$thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
$thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
$thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
$thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
$thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
$thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
$thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
$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'];
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) {
if ($key != 'streams') {
$ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
}
}
}
break;
case 'logical-fileinfo':
// shortcut
$thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
$thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
$thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
$thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
//$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
//$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
//$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
break;
}
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 ($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;
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;
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;
break;
}
$ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0);
}
}
break;
case 'CONT': // Content Description Header (text comments)
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
$thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
$offset += $thisfile_real_chunks_currentchunk['title_len'];
$thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
$offset += $thisfile_real_chunks_currentchunk['artist_len'];
$thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
$offset += $thisfile_real_chunks_currentchunk['copyright_len'];
$thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
$offset += $thisfile_real_chunks_currentchunk['comment_len'];
$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]);
}
}
}
break;
case 'DATA': // Data Chunk Header
// do nothing
break;
case 'INDX': // Index Section Header
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
$thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
$offset += 2;
$thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
$offset += 4;
if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
// last index chunk found, ignore rest of file
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);
}
}
break;
default:
$ThisFileInfo['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'];
}
}
return true;
}
function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
$ParsedArray = array();
$ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
if ($ParsedArray['magic'] != '.ra'."\xFD") {
return false;
}
$ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
if ($ParsedArray['version1'] < 3) {
return false;
} elseif ($ParsedArray['version1'] == 3) {
$ParsedArray['fourcc1'] = '.ra3';
$ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
$ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
//$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
//$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
$commentoffset = 0;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
$commentoffset++; // final null terminator (?)
$commentoffset++; // fourcc length (?) should be 4
$ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
} elseif ($ParsedArray['version1'] <= 5) {
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
$ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
$ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
$ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
$ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
$ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
//$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
$ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
$ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
$ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
//$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
switch ($ParsedArray['version1']) {
case 4:
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
//$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
$ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
$ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
$ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
//$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
//$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
$commentoffset = 0;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
$commentoffset += $commentlength;
break;
case 5:
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
$ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
$ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
$ParsedArray['comments'] = array();
break;
}
$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
}
foreach ($ParsedArray['comments'] as $key => $value) {
if ($ParsedArray['comments'][$key][0] === false) {
$ParsedArray['comments'][$key][0] = '';
}
}
return true;
}
function RealAudioCodecFourCClookup($fourcc, $bitrate) {
static $RealAudioCodecFourCClookup = array();
if (empty($RealAudioCodecFourCClookup)) {
// http://www.its.msstate.edu/net/real/reports/config/tags.stats
// http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
$RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
$RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
$RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
$RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
$RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
$RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
$RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
$RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
$RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
$RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
$RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
$RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
$RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
$RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
$RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
$RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
$RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
$RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
$RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
$RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
$RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
$RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
$RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
}
$roundbitrate = intval(round($bitrate));
if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
} elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
return $RealAudioCodecFourCClookup[$fourcc][0];
}
return $fourcc;
}
}
?>
File diff suppressed because it is too large Load Diff
+149
View File
@@ -0,0 +1,149 @@
<?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-video.swf.php //
// module for analyzing Shockwave Flash files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_swf
{
function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
//$start_time = microtime(true);
$ThisFileInfo['fileformat'] = 'swf';
$ThisFileInfo['video']['dataformat'] = 'swf';
// http://www.openswf.org/spec/SWFfileformat.html
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['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']) {
case 'FWS':
$ThisFileInfo['swf']['header']['compressed'] = false;
break;
case 'CWS':
$ThisFileInfo['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']);
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']) {
$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)';
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);
$FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT);
for ($i = 1; $i < $FrameSizeDataLength; $i++) {
$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);
// 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.
// It is supposed to be stored as a 16bit integer, but the first byte
// (or last depending on how you look at it) is completely ignored.
// 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));
$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;
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'];
}
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
// SWF tags
$CurrentOffset = 12 + $FrameSizeDataLength;
$SWFdataLength = strlen($SWFfileData);
while ($CurrentOffset < $SWFdataLength) {
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
$TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
$TagID = ($TagIDTagLength & 0xFFFC) >> 6;
$TagLength = ($TagIDTagLength & 0x003F);
$CurrentOffset += 2;
if ($TagLength == 0x3F) {
$TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
$CurrentOffset += 4;
}
unset($TagData);
$TagData['offset'] = $CurrentOffset;
$TagData['size'] = $TagLength;
$TagData['id'] = $TagID;
$TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
switch ($TagID) {
case 0: // end of movie
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));
break;
default:
if ($ReturnAllTagData) {
$ThisFileInfo['swf']['tags'][] = $TagData;
}
break;
}
$CurrentOffset += $TagLength;
}
return true;
}
}
?>
+542
View File
@@ -0,0 +1,542 @@
<?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
{
// new combined constructor
function getid3_aac(&$fd, &$ThisFileInfo, $option) {
if ($option === 'adif') {
$this->getAACADIFheaderFilepointer($fd, $ThisFileInfo);
}
elseif ($option === 'adts') {
$this->getAACADTSheaderFilepointer($fd, $ThisFileInfo);
}
}
function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'aac';
$ThisFileInfo['audio']['dataformat'] = 'aac';
$ThisFileInfo['audio']['lossless'] = false;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AACheader = fread($fd, 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;
$ThisFileInfo['aac']['header_type'] = 'ADIF';
$bitoffset += 32;
$ThisFileInfo['aac']['header']['mpeg_version'] = 4;
$ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
$bitoffset += 1;
if ($ThisFileInfo['aac']['header']['copyright']) {
$ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
$bitoffset += 72;
}
$ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
$bitoffset += 1;
$ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
$bitoffset += 1;
$ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
$bitoffset += 1;
if ($ThisFileInfo['aac']['header']['is_vbr']) {
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
$bitoffset += 23;
} else {
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
$bitoffset += 23;
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate'];
}
if ($ThisFileInfo['audio']['bitrate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
return false;
}
$ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) {
// 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 (!$ThisFileInfo['aac']['header']['is_vbr']) {
$ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
$bitoffset += 20;
}
$ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
$ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
$bitoffset += 3;
$ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) {
$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) {
$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
$ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
$ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
}
$bitoffset = ceil($bitoffset / 8) * 8;
$ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
$bitoffset += 8;
$ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']));
$bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'];
$ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']);
$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']);
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'];
$ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]);
if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) {
$ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field'];
}
}
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
return true;
} else {
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['aac']);
$ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
return false;
}
}
function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
// 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
// * ADTS Fixed Header: these don't change from frame to frame
// syncword 12 always: '111111111111'
// ID 1 0: MPEG-4, 1: MPEG-2
// layer 2 always: '00'
// protection_absent 1
// profile 2
// sampling_frequency_index 4
// private_bit 1
// channel_configuration 3
// original/copy 1
// home 1
// emphasis 2 only if ID == 0 (ie MPEG-4)
// * 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 = 0;
$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 ($byteoffset >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond '.ftell($fd).' (PHP does not support file operations beyond 2GB)';
return false;
}
fseek($fd, $byteoffset, SEEK_SET);
// First get substring
$substring = fread($fd, 10);
$substringlength = strlen($substring);
if ($substringlength != 10) {
$ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)';
return false;
}
// Initialise $AACheaderBitstream
$AACheaderBitstream = '';
// Loop thru substring chars
for ($i = 0; $i < 10; $i++) {
$AACheaderBitstream .= $decbin[$substring{$i}];
}
$bitoffset = 0;
$synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12));
$bitoffset += 12;
if ($synctest != 0x0FFF) {
$ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)';
if ($ThisFileInfo['fileformat'] == 'aac') {
return true;
}
return false;
}
// Gather info for first frame only - this takes time to do 1000 times!
if ($framenumber > 0) {
if (!$AACheaderBitstream[$bitoffset]) {
// MPEG-4
$bitoffset += 20;
} else {
// MPEG-2
$bitoffset += 18;
}
} else {
$ThisFileInfo['aac']['header_type'] = 'ADTS';
$ThisFileInfo['aac']['header']['synch'] = $synctest;
$ThisFileInfo['fileformat'] = 'aac';
$ThisFileInfo['audio']['dataformat'] = 'aac';
$ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2);
$bitoffset += 1;
$ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
if ($ThisFileInfo['aac']['header']['layer'] != 0) {
$ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead';
return false;
}
$ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false);
$bitoffset += 1;
$ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
$ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']);
$ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
$bitoffset += 4;
$ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']);
if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero';
return false;
}
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency'];
$ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
$bitoffset += 3;
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration'];
$ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) {
$ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
}
if ($ReturnExtendedInfo) {
$ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
$ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
$bitoffset += 1;
} else {
$bitoffset += 2;
}
}
$FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13));
if (!isset($BitrateCache[$FrameLength])) {
$BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
}
@$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++;
$ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
$bitoffset += 13;
$ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11));
$bitoffset += 11;
if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
} else {
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
}
$ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2));
$bitoffset += 2;
if ($ThisFileInfo['aac']['header']['crc_present']) {
//$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16));
$bitoffset += 16;
}
if (!$ReturnExtendedInfo) {
unset($ThisFileInfo['aac'][$framenumber]);
}
$byteoffset += $FrameLength;
if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) {
// keep scanning
} else {
$ThisFileInfo['aac']['frames'] = $framenumber;
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
if ($ThisFileInfo['playtime_seconds'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
return false;
}
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
ksort($ThisFileInfo['aac']['bitrate_distribution']);
$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
return true;
}
}
// should never get here.
}
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');
}
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');
}
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;
}
}
?>
+497
View File
@@ -0,0 +1,497 @@
<?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
{
function getid3_ac3(&$fd, &$ThisFileInfo) {
///AH
$ThisFileInfo['ac3']['raw']['bsi'] = array();
$thisfile_ac3 = &$ThisFileInfo['ac3'];
$thisfile_ac3_raw = &$thisfile_ac3['raw'];
$thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
// http://www.atsc.org/standards/a_52a.pdf
$ThisFileInfo['fileformat'] = 'ac3';
$ThisFileInfo['audio']['dataformat'] = 'ac3';
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$ThisFileInfo['audio']['lossless'] = false;
// 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($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AC3header['syncinfo'] = fread($fd, 5);
$thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2);
if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") {
$ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead';
unset($thisfile_ac3);
return false;
} else {
// syncinfo() {
// syncword 16
// crc1 16
// fscod 2
// frmsizecod 6
// } /* end of syncinfo */
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2));
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($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) {
$ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
}
$thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
$thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
$ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate'];
$AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15));
$ac3_bsi_offset = 0;
$thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
$ac3_bsi_offset += 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.
$ThisFileInfo['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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
$ac3_bsi_offset += 3;
$thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
$ac3_bsi_offset += 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:
$ThisFileInfo['audio']['channelmode'] = 'mono';
break;
case 3:
case 4:
$ThisFileInfo['audio']['channelmode'] = 'stereo';
break;
default:
$ThisFileInfo['audio']['channelmode'] = 'surround';
break;
}
$ThisFileInfo['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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$ac3_bsi_offset += 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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$ac3_bsi_offset += 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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$ac3_bsi_offset += 2;
$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
}
$thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
if ($thisfile_ac3_raw_bsi['lfeon']) {
//$ThisFileInfo['audio']['channels']++;
$ThisFileInfo['audio']['channels'] .= '.1';
}
$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 131.
// 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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
$ac3_bsi_offset += 5;
$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
$thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['compre_flag']) {
$thisfile_ac3_raw_bsi['compr'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
$ac3_bsi_offset += 8;
$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']);
}
$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['langcode_flag']) {
$thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
$ac3_bsi_offset += 8;
}
$thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['audprodie']) {
$thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
$ac3_bsi_offset += 5;
$thisfile_ac3_raw_bsi['roomtyp'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$ac3_bsi_offset += 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 131.
// 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'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
$ac3_bsi_offset += 5;
$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['compre_flag2']) {
$thisfile_ac3_raw_bsi['compr2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
$ac3_bsi_offset += 8;
$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']);
}
$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
$thisfile_ac3_raw_bsi['langcod2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
$ac3_bsi_offset += 8;
}
$thisfile_ac3_raw_bsi['audprodie2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['audprodie2']) {
$thisfile_ac3_raw_bsi['mixlevel2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
$ac3_bsi_offset += 5;
$thisfile_ac3_raw_bsi['roomtyp2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$ac3_bsi_offset += 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) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
$thisfile_ac3_raw_bsi['original'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
$thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
$thisfile_ac3_raw_bsi['timecode1'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
$ac3_bsi_offset += 14;
}
$thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
$thisfile_ac3_raw_bsi['timecode2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
$ac3_bsi_offset += 14;
}
$thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
$thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6));
$ac3_bsi_offset += 6;
$AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length']));
$thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
$ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
}
}
return true;
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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;
}
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;
}
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);
}
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);
}
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);
}
}
?>
+163
View File
@@ -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 //
/////////////////////////////////////////////////////////////////
// //
// module.audio.au.php //
// module for analyzing AU files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_au
{
function getid3_au(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AUheader = fread($fd, 8);
if (substr($AUheader, 0, 4) != '.snd') {
$ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"';
return false;
}
// shortcut
$ThisFileInfo['au'] = array();
$thisfile_au = &$ThisFileInfo['au'];
$ThisFileInfo['fileformat'] = 'au';
$ThisFileInfo['audio']['dataformat'] = 'au';
$ThisFileInfo['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'];
$thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
$thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
$thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
$thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
$thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
$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'];
} else {
unset($thisfile_au['bits_per_sample']);
}
$ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate'];
$ThisFileInfo['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"';
}
$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'];
return true;
}
function AUdataFormatNameLookup($id) {
static $AUdataFormatNameLookup = array(
0 => 'unspecified format',
1 => '8-bit mu-law',
2 => '8-bit linear',
3 => '16-bit linear',
4 => '24-bit linear',
5 => '32-bit linear',
6 => 'floating-point',
7 => 'double-precision float',
8 => 'fragmented sampled data',
9 => 'SUN_FORMAT_NESTED',
10 => 'DSP program',
11 => '8-bit fixed-point',
12 => '16-bit fixed-point',
13 => '24-bit fixed-point',
14 => '32-bit fixed-point',
16 => 'non-audio display data',
17 => 'SND_FORMAT_MULAW_SQUELCH',
18 => '16-bit linear with emphasis',
19 => '16-bit linear with compression',
20 => '16-bit linear with emphasis + compression',
21 => 'Music Kit DSP commands',
22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
23 => 'CCITT g.721 4-bit ADPCM',
24 => 'CCITT g.722 ADPCM',
25 => 'CCITT g.723 3-bit ADPCM',
26 => 'CCITT g.723 5-bit ADPCM',
27 => 'A-Law 8-bit'
);
return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
}
function AUdataFormatBitsPerSampleLookup($id) {
static $AUdataFormatBitsPerSampleLookup = array(
1 => 8,
2 => 8,
3 => 16,
4 => 24,
5 => 32,
6 => 32,
7 => 64,
11 => 8,
12 => 16,
13 => 24,
14 => 32,
18 => 16,
19 => 16,
20 => 16,
23 => 16,
25 => 16,
26 => 16,
27 => 8
);
return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
}
function AUdataFormatUsedBitsPerSampleLookup($id) {
static $AUdataFormatUsedBitsPerSampleLookup = array(
1 => 8,
2 => 8,
3 => 16,
4 => 24,
5 => 32,
6 => 32,
7 => 64,
11 => 8,
12 => 16,
13 => 24,
14 => 32,
18 => 16,
19 => 16,
20 => 16,
23 => 4,
25 => 3,
26 => 5,
27 => 8,
);
return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
}
}
?>
+125
View File
@@ -0,0 +1,125 @@
<?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
{
function getid3_avr(&$fd, &$ThisFileInfo) {
// 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
$ThisFileInfo['fileformat'] = 'avr';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AVRheader = fread($fd, 128);
$ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') {
$ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['avr']);
return false;
}
$ThisFileInfo['avdataoffset'] += 128;
$ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
$ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
$ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
$ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
$ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
$ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
$ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
$ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
$ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
$ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
$ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
$ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
$ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
$ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
$ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
$ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
$ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true);
$ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true);
$ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true);
$ThisFileInfo['avr']['midi_notes'] = array();
if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8;
}
if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF);
}
if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
$ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
}
$ThisFileInfo['audio']['dataformat'] = 'avr';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample'];
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate'];
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1);
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate'];
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds'];
return true;
}
}
?>
+221
View File
@@ -0,0 +1,221 @@
<?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 BONK audio files //
// dependencies: module.tag.id3v2.php (optional) //
// ///
/////////////////////////////////////////////////////////////////
class getid3_bonk
{
function getid3_bonk(&$fd, &$ThisFileInfo) {
// shortcut
$ThisFileInfo['bonk'] = array();
$thisfile_bonk = &$ThisFileInfo['bonk'];
$thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset'];
$thisfile_bonk['dataend'] = $ThisFileInfo['avdataend'];
if ($thisfile_bonk['dataend'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to 2GB';
} else {
// scan-from-end method, for v0.6 and higher
fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET);
$PossibleBonkTag = fread($fd, 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);
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.'"';
return false;
}
$BonkTagName = substr($TagHeaderTest, 1, 4);
$thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
$thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
$NextTagEndOffset = $BonkTagOffset - 8;
if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
if (empty($ThisFileInfo['audio']['encoder'])) {
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
}
return true;
}
fseek($fd, $NextTagEndOffset, SEEK_SET);
$PossibleBonkTag = fread($fd, 8);
}
}
// seek-from-beginning method for v0.4 and v0.5
if (empty($thisfile_bonk['BONK'])) {
fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET);
do {
$TagHeaderTest = fread($fd, 5);
switch ($TagHeaderTest) {
case "\x00".'BONK':
if (empty($ThisFileInfo['audio']['encoder'])) {
$ThisFileInfo['audio']['encoder'] = 'BONK v0.4';
}
break;
case "\x00".'INFO':
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5';
break;
default:
break 2;
}
$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);
} 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);
if ($TagHeaderTest == "\x00".'INFO') {
$ThisFileInfo['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);
}
}
if (empty($ThisFileInfo['audio']['encoder'])) {
$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
}
if (empty($thisfile_bonk['BONK'])) {
unset($ThisFileInfo['bonk']);
}
return true;
}
function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) {
switch ($BonkTagName) {
case 'BONK':
// shortcut
$thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK'];
$BonkData = "\x00".'BONK'.fread($fd, 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));
$thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1));
$thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1));
$thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1));
$thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2));
$thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1));
$thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2));
$ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
$ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
$ThisFileInfo['fileformat'] = 'bonk';
$ThisFileInfo['audio']['dataformat'] = 'bonk';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // assumed
$ThisFileInfo['audio']['channels'] = $thisfile_bonk_BONK['channels'];
$ThisFileInfo['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
$ThisFileInfo['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
$ThisFileInfo['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
$ThisFileInfo['audio']['codec'] = 'bonk';
$ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
if ($ThisFileInfo['playtime_seconds'] > 0) {
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
}
break;
case 'INFO':
// shortcut
$thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO'];
$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
$thisfile_bonk_INFO['entries_count'] = 0;
$NextInfoDataPair = fread($fd, 5);
if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
while (!feof($fd)) {
//$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);
if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
fseek($fd, -5, SEEK_CUR);
break;
}
$thisfile_bonk_INFO['entries_count']++;
}
}
break;
case 'META':
$BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5);
$ThisFileInfo['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);
$offset += 4;
$MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
$offset += 4;
$ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
}
break;
case ' ID3':
$ThisFileInfo['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);
}
break;
default:
$ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset'];
break;
}
}
function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
foreach ($BonkIsValidTagName as $validtagname) {
if ($validtagname == $PossibleBonkTag) {
return true;
} elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
return true;
}
}
return false;
}
}
?>
+72
View File
@@ -0,0 +1,72 @@
<?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.au.php //
// module for analyzing Digital Speech Standard (DSS) files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_dss
{
function getid3_dss(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$DSSheader = fread($fd, 1256);
if (substr($DSSheader, 0, 4) != "\x02".'dss') {
$ThisFileInfo['error'][] = 'Expecting "[x02]dss" at offset '.$ThisFileInfo['avdataoffset'].', found "'.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'];
$ThisFileInfo['fileformat'] = 'dss';
$ThisFileInfo['audio']['dataformat'] = 'dss';
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
//$thisfile_dss['encoding'] = 'ISO-8859-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['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;
$ThisFileInfo['playtime_seconds'] = $thisfile_dss['length'];
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['filesize'] * 8) / $ThisFileInfo['playtime_seconds'];
return true;
}
function DSSdateStringToUnixDate($datestring) {
$y = substr($datestring, 0, 2);
$m = substr($datestring, 2, 2);
$d = substr($datestring, 4, 2);
$h = substr($datestring, 6, 2);
$i = substr($datestring, 8, 2);
$s = substr($datestring, 10, 2);
$y += (($y < 95) ? 2000 : 1900);
return mktime($h, $i, $s, $m, $d, $y);
}
}
?>
+239
View File
@@ -0,0 +1,239 @@
<?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
{
function getid3_dts(&$fd, &$ThisFileInfo) {
// Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)"
// (http://pda.etsi.org/pda/queryform.asp)
// With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/
$ThisFileInfo['fileformat'] = 'dts';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$DTSheader = fread($fd, 16);
$ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4));
if ($ThisFileInfo['dts']['raw']['magic'] != 0x7FFE8001) {
$ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset '.$ThisFileInfo['avdataoffset'].', found "0x'.str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT).'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['dts']);
return false;
}
$fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
$bsOffset = 0;
$ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5;
$ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7;
$ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14;
$ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6)); $bsOffset += 6;
$ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
$ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5;
$ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3)); $bsOffset += 3;
$ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
$ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
if ($ThisFileInfo['dts']['flags']['crc_present']) {
$ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16;
}
$ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
$ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
$ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2)); $bsOffset += 2;
$ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4)); $bsOffset += 4;
$ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
$ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
$ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
$ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
$ThisFileInfo['dts']['flags']['lossless'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 31) ? true : false);
$ThisFileInfo['dts']['bitrate_mode'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
$ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
$ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
$ThisFileInfo['audio']['dataformat'] = 'dts';
$ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless'];
$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample'];
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels'];
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate'];
if (isset($ThisFileInfo['avdataend'])) {
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
}
return true;
}
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 @$DTSbitrateLookup[$index];
}
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 @$DTSsampleRateLookup[$index];
}
function DTSbitPerSampleLookup($index) {
$DTSbitPerSampleLookup = array(
0 => 16,
1 => 20,
2 => 24,
3 => 24,
);
return @$DTSbitPerSampleLookup[$index];
}
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;
}
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 (@$DTSchannelArrangementLookup[$index] ? @$DTSchannelArrangementLookup[$index] : 'user-defined');
}
function DTSdialogNormalization($index, $version) {
switch ($version) {
case 7:
return 0 - $index;
break;
case 6:
return 0 - 16 - $index;
break;
}
return false;
}
}
?>
+397
View File
@@ -0,0 +1,397 @@
<?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
{
function getid3_flac(&$fd, &$ThisFileInfo) {
// http://flac.sourceforge.net/format.html
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$StreamMarker = fread($fd, 4);
if ($StreamMarker != 'fLaC') {
$ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
return false;
}
$ThisFileInfo['fileformat'] = 'flac';
$ThisFileInfo['audio']['dataformat'] = 'flac';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['audio']['lossless'] = true;
return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo);
}
function FLACparseMETAdata(&$fd, &$ThisFileInfo) {
do {
$METAdataBlockOffset = ftell($fd);
$METAdataBlockHeader = fread($fd, 4);
$METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80);
$METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F;
$METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
$METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
if ($METAdataBlockLength < 0) {
$ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
break;
}
$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array();
$ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['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;
$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = @fread($fd, $METAdataBlockLength);
$ThisFileInfo['avdataoffset'] = ftell($fd);
switch ($METAdataBlockTypeText) {
case 'STREAMINFO':
if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'PADDING':
// ignore
break;
case 'APPLICATION':
if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'SEEKTABLE':
if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'VORBIS_COMMENT':
$OldOffset = ftell($fd);
fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
fseek($fd, $OldOffset, SEEK_SET);
break;
case 'CUESHEET':
if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'PICTURE':
if (!$this->FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
default:
$ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
break;
}
} while ($METAdataLastBlockFlag === false);
if (isset($ThisFileInfo['flac']['STREAMINFO'])) {
$ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
$ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8);
if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero';
return false;
}
$ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes'];
}
// set md5_data_source - built into flac 0.5+
if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) {
if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
$ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
} else {
$ThisFileInfo['md5_data_source'] = '';
$md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature'];
for ($i = 0; $i < strlen($md5); $i++) {
$ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
}
if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
unset($ThisFileInfo['md5_data_source']);
}
}
}
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
if ($ThisFileInfo['audio']['bits_per_sample'] == 8) {
// special case
// must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
// MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
$ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file';
}
if (!empty($ThisFileInfo['ogg']['vendor'])) {
$ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor'];
}
return true;
}
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');
}
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');
}
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, &$ThisFileInfo) {
$offset = 0;
$ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
$offset += 2;
$ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
$offset += 2;
$ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
$offset += 3;
$ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
$offset += 3;
$SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
$ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20));
$ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1;
$ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1;
$ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
$offset += 8;
$ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
$offset += 16;
if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
} else {
$ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
return false;
}
unset($ThisFileInfo['flac']['STREAMINFO']['raw']);
return true;
}
function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) {
$offset = 0;
$ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
$offset += 4;
$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
$offset = $METAdataBlockLength;
unset($ThisFileInfo['flac']['APPLICATION']['raw']);
return true;
}
function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) {
$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
@$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++;
$offset += 10;
} else {
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
$offset += 8;
$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
$offset += 2;
}
}
unset($ThisFileInfo['flac']['SEEKTABLE']['raw']);
return true;
}
function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) {
$offset = 0;
$ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0");
$offset += 128;
$ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
$offset += 8;
$ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
$offset += 1;
$offset += 258; // reserved
$ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
$offset += 1;
for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) {
$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
$offset += 8;
$TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
$offset += 1;
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12);
$offset += 12;
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
$offset += 1;
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
$offset += 13; // reserved
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
$offset += 1;
for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
$offset += 8;
$IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
$offset += 1;
$offset += 3; // reserved
$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
}
}
unset($ThisFileInfo['flac']['CUESHEET']['raw']);
return true;
}
function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) {
$picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1];
$offset = 0;
$picture['type'] = $this->FLACpictureTypeLookup(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['mime_type'] = 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['image_data'] = substr($meta_data_block_data, $offset, $length);
$offset += $length;
unset($ThisFileInfo['flac']['PICTURE']['raw']);
return true;
}
}
?>
+228
View File
@@ -0,0 +1,228 @@
<?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 audio files //
// dependencies: module.audio.riff.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_la
{
function getid3_la(&$fd, &$ThisFileInfo) {
$offset = 0;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
switch (substr($rawdata, $offset, 4)) {
case 'LA02':
case 'LA03':
case 'LA04':
$ThisFileInfo['fileformat'] = 'la';
$ThisFileInfo['audio']['dataformat'] = 'la';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
$ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
$ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10);
$offset += 4;
$ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
if ($ThisFileInfo['la']['uncompressed_size'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero';
return false;
}
$WAVEchunk = substr($rawdata, $offset, 4);
if ($WAVEchunk !== 'WAVE') {
$ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
return false;
}
$offset += 4;
$ThisFileInfo['la']['fmt_size'] = 24;
if ($ThisFileInfo['la']['version'] >= 0.3) {
$ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24;
$offset += 4;
} else {
// version 0.2 didn't support additional data blocks
$ThisFileInfo['la']['header_size'] = 41;
}
$fmt_chunk = substr($rawdata, $offset, 4);
if ($fmt_chunk !== 'fmt ') {
$ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.';
return false;
}
$offset += 4;
$fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
$ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$offset += 2;
$ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$offset += 2;
if ($ThisFileInfo['la']['channels'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero';
return false;
}
$ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
if ($ThisFileInfo['la']['sample_rate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero';
return false;
}
$ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
$ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$offset += 2;
$ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$offset += 2;
$ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
$ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
$offset += 1;
$ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01);
if ($ThisFileInfo['la']['version'] >= 0.4) {
$ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02);
}
$ThisFileInfo['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 ($ThisFileInfo['la']['version'] >= 0.4) {
$ThisFileInfo['la']['blocksize'] = 61440;
$ThisFileInfo['la']['seekevery'] = 19;
} else {
$ThisFileInfo['la']['blocksize'] = 73728;
$ThisFileInfo['la']['seekevery'] = 16;
}
$ThisFileInfo['la']['seekpoint_count'] = 0;
if ($ThisFileInfo['la']['flags']['seekable']) {
$ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery']));
for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) {
$ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
}
}
if ($ThisFileInfo['la']['version'] >= 0.3) {
// Following the main header information, the program outputs all of the
// seekpoints. Following these is what I called the 'footer start',
// i.e. the position immediately after the La audio data is finished.
$ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) {
$ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')';
$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize'];
}
} else {
// La v0.2 didn't have FooterStart value
$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend'];
}
if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
if ($RIFFtempfilename = tempnam('*', 'id3')) {
if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
$RIFFdata = 'WAVE';
if ($ThisFileInfo['la']['version'] == 0.2) {
$RIFFdata .= substr($rawdata, 12, 24);
} else {
$RIFFdata .= substr($rawdata, 16, 24);
}
if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET);
$RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']);
}
$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
$dummy = $ThisFileInfo;
$dummy['filesize'] = strlen($RIFFdata);
$dummy['avdataoffset'] = 0;
$dummy['avdataend'] = $dummy['filesize'];
$riff = new getid3_riff($RIFF_fp, $dummy);
if (empty($dummy['error'])) {
$ThisFileInfo['riff'] = $dummy['riff'];
} else {
$ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']);
}
unset($riff);
unset($dummy);
fclose($RIFF_fp);
}
unlink($RIFFtempfilename);
}
}
// $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
$ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart'];
$ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset;
//$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']);
$ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']);
$ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels'];
if ($ThisFileInfo['playtime_seconds'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero';
return false;
}
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
//$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample'];
break;
default:
if (substr($rawdata, $offset, 2) == 'LA') {
$ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.';
} else {
$ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file';
}
return false;
break;
}
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels'];
$ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate'];
$ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version'];
return true;
}
}
?>
+126
View File
@@ -0,0 +1,126 @@
<?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
{
function getid3_lpac(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$LPACheader = fread($fd, 14);
if (substr($LPACheader, 0, 4) != 'LPAC') {
$ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
return false;
}
$ThisFileInfo['avdataoffset'] += 14;
$ThisFileInfo['fileformat'] = 'lpac';
$ThisFileInfo['audio']['dataformat'] = 'lpac';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
$flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
$ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
$ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
$ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04);
$ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02);
$ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01);
if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) {
$ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set';
}
$ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
$ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
$ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
$ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
$ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
$ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
$ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
$ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) {
$ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"';
}
switch ($ThisFileInfo['lpac']['file_version']) {
case 6:
if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) {
$ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
}
if ($ThisFileInfo['lpac']['quantization'] != 20) {
$ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q'];
}
break;
default:
//$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org';
break;
}
$dummy = $ThisFileInfo;
$riff = new getid3_riff($fd, $dummy);
unset($riff);
$ThisFileInfo['avdataoffset'] = $dummy['avdataoffset'];
$ThisFileInfo['riff'] = $dummy['riff'];
$ThisFileInfo['error'] = $dummy['error'];
$ThisFileInfo['warning'] = $dummy['warning'];
$ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments'];
$ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate'];
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
if ($ThisFileInfo['lpac']['flags']['24_bit']) {
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
} elseif ($ThisFileInfo['lpac']['flags']['16_bit']) {
$ThisFileInfo['audio']['bits_per_sample'] = 16;
} else {
$ThisFileInfo['audio']['bits_per_sample'] = 8;
}
if ($ThisFileInfo['lpac']['flags']['fast_compress']) {
// fast
$ThisFileInfo['audio']['encoder_options'] = '-1';
} else {
switch ($ThisFileInfo['lpac']['max_prediction_order']) {
case 20: // simple
$ThisFileInfo['audio']['encoder_options'] = '-2';
break;
case 30: // medium
$ThisFileInfo['audio']['encoder_options'] = '-3';
break;
case 40: // high
$ThisFileInfo['audio']['encoder_options'] = '-4';
break;
case 60: // extrahigh
$ThisFileInfo['audio']['encoder_options'] = '-5';
break;
}
}
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
return true;
}
}
?>
+522
View File
@@ -0,0 +1,522 @@
<?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.midi.php //
// module for Midi Audio files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_midi
{
function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) {
// shortcut
$ThisFileInfo['midi']['raw'] = array();
$thisfile_midi = &$ThisFileInfo['midi'];
$thisfile_midi_raw = &$thisfile_midi['raw'];
$ThisFileInfo['fileformat'] = 'midi';
$ThisFileInfo['audio']['dataformat'] = 'midi';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$MIDIdata = fread($fd, 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']);
return false;
}
$offset += 4;
$thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
$offset += 4;
$thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
$offset += 2;
$thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
$offset += 2;
$thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
$offset += 2;
for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
if ((strlen($MIDIdata) - $offset) < 8) {
$MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
}
$trackID = substr($MIDIdata, $offset, 4);
$offset += 4;
if ($trackID == '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';
return false;
}
}
if (!isset($trackdataarray) || !is_array($trackdataarray)) {
$ThisFileInfo['error'][] = 'Cannot find MIDI track information';
unset($thisfile_midi);
unset($ThisFileInfo['fileformat']);
return false;
}
if ($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;
$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 ();
foreach ($trackdataarray as $tracknumber => $trackdata) {
$eventsoffset = 0;
$LastIssuedMIDIcommand = 0;
$LastIssuedMIDIchannel = 0;
$CumulativeDeltaTime = 0;
$TicksAtCurrentBPM = 0;
while ($eventsoffset < strlen($trackdata)) {
$eventid = 0;
if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
$eventid = count($MIDIevents[$tracknumber]);
}
$deltatime = 0;
for ($i = 0; $i < 4; $i++) {
$deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
$deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
if ($deltatimebyte & 0x80) {
// another byte follows
} else {
break;
}
}
$CumulativeDeltaTime += $deltatime;
$TicksAtCurrentBPM += $deltatime;
$MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
$MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
if ($MIDI_event_channel & 0x80) {
// OK, normal event - MIDI command has MSB set
$LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
$LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
} else {
// running event - assume last command
$eventsoffset--;
}
$MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
$MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released)
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed)
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change
$controllernum = ord(substr($trackdata, $eventsoffset++, 1));
$newvalue = ord(substr($trackdata, $eventsoffset++, 1));
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change
$newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
$thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
if ($tracknumber == 10) {
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
} else {
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
}
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch
$channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change)
$changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
$changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
} elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
$METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
$METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
$METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
$eventsoffset += $METAeventLength;
switch ($METAeventCommand) {
case 0x00: // Set track sequence number
$track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
break;
case 0x01: // Text: generic
$text_generic = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
$thisfile_midi['comments']['comment'][] = $text_generic;
break;
case 0x02: // Text: copyright
$text_copyright = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
$thisfile_midi['comments']['copyright'][] = $text_copyright;
break;
case 0x03: // Text: track name
$text_trackname = substr($METAeventData, 0, $METAeventLength);
$thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
break;
case 0x04: // Text: track instrument name
$text_instrument = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
break;
case 0x05: // Text: lyrics
$text_lyrics = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
if (!isset($thisfile_midi['lyrics'])) {
$thisfile_midi['lyrics'] = '';
}
$thisfile_midi['lyrics'] .= $text_lyrics."\n";
break;
case 0x06: // Text: marker
$text_marker = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
break;
case 0x07: // Text: cue point
$text_cuepoint = substr($METAeventData, 0, $METAeventLength);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
break;
case 0x2F: // End Of Track
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
break;
case 0x51: // Tempo: microseconds / quarter note
$CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
if ($CurrentMicroSecondsPerBeat == 0) {
$ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
return false;
}
$thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
$CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
$MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
$TicksAtCurrentBPM = 0;
break;
case 0x58: // Time signature
$timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
$timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator;
$thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
break;
case 0x59: // Keysignature
$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
if ($keysig_sharpsflats & 0x80) {
// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
$keysig_sharpsflats -= 256;
}
$keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
// $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
$thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
break;
case 0x7F: // Sequencer specific information
$custom_data = substr($METAeventData, 0, $METAeventLength);
break;
default:
$ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand;
break;
}
} else {
$ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel'];
}
}
if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
$thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
}
}
$previoustickoffset = null;
ksort($MicroSecondsPerQuarterNoteAfter);
foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
if (is_null($previoustickoffset)) {
$prevmicrosecondsperbeat = $microsecondsperbeat;
$previoustickoffset = $tickoffset;
continue;
}
if ($thisfile_midi['totalticks'] > $tickoffset) {
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
return false;
}
$ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
$prevmicrosecondsperbeat = $microsecondsperbeat;
$previoustickoffset = $tickoffset;
}
}
if ($thisfile_midi['totalticks'] > $previoustickoffset) {
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
return false;
}
$ThisFileInfo['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($thisfile_midi['lyrics'])) {
$thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
}
return true;
}
function GeneralMIDIinstrumentLookup($instrumentid) {
$begin = __LINE__;
/** This is not a comment!
0 Acoustic Grand
1 Bright Acoustic
2 Electric Grand
3 Honky-Tonk
4 Electric Piano 1
5 Electric Piano 2
6 Harpsichord
7 Clavier
8 Celesta
9 Glockenspiel
10 Music Box
11 Vibraphone
12 Marimba
13 Xylophone
14 Tubular Bells
15 Dulcimer
16 Drawbar Organ
17 Percussive Organ
18 Rock Organ
19 Church Organ
20 Reed Organ
21 Accordian
22 Harmonica
23 Tango Accordian
24 Acoustic Guitar (nylon)
25 Acoustic Guitar (steel)
26 Electric Guitar (jazz)
27 Electric Guitar (clean)
28 Electric Guitar (muted)
29 Overdriven Guitar
30 Distortion Guitar
31 Guitar Harmonics
32 Acoustic Bass
33 Electric Bass (finger)
34 Electric Bass (pick)
35 Fretless Bass
36 Slap Bass 1
37 Slap Bass 2
38 Synth Bass 1
39 Synth Bass 2
40 Violin
41 Viola
42 Cello
43 Contrabass
44 Tremolo Strings
45 Pizzicato Strings
46 Orchestral Strings
47 Timpani
48 String Ensemble 1
49 String Ensemble 2
50 SynthStrings 1
51 SynthStrings 2
52 Choir Aahs
53 Voice Oohs
54 Synth Voice
55 Orchestra Hit
56 Trumpet
57 Trombone
58 Tuba
59 Muted Trumpet
60 French Horn
61 Brass Section
62 SynthBrass 1
63 SynthBrass 2
64 Soprano Sax
65 Alto Sax
66 Tenor Sax
67 Baritone Sax
68 Oboe
69 English Horn
70 Bassoon
71 Clarinet
72 Piccolo
73 Flute
74 Recorder
75 Pan Flute
76 Blown Bottle
77 Shakuhachi
78 Whistle
79 Ocarina
80 Lead 1 (square)
81 Lead 2 (sawtooth)
82 Lead 3 (calliope)
83 Lead 4 (chiff)
84 Lead 5 (charang)
85 Lead 6 (voice)
86 Lead 7 (fifths)
87 Lead 8 (bass + lead)
88 Pad 1 (new age)
89 Pad 2 (warm)
90 Pad 3 (polysynth)
91 Pad 4 (choir)
92 Pad 5 (bowed)
93 Pad 6 (metallic)
94 Pad 7 (halo)
95 Pad 8 (sweep)
96 FX 1 (rain)
97 FX 2 (soundtrack)
98 FX 3 (crystal)
99 FX 4 (atmosphere)
100 FX 5 (brightness)
101 FX 6 (goblins)
102 FX 7 (echoes)
103 FX 8 (sci-fi)
104 Sitar
105 Banjo
106 Shamisen
107 Koto
108 Kalimba
109 Bagpipe
110 Fiddle
111 Shanai
112 Tinkle Bell
113 Agogo
114 Steel Drums
115 Woodblock
116 Taiko Drum
117 Melodic Tom
118 Synth Drum
119 Reverse Cymbal
120 Guitar Fret Noise
121 Breath Noise
122 Seashore
123 Bird Tweet
124 Telephone Ring
125 Helicopter
126 Applause
127 Gunshot
*/
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
}
function GeneralMIDIpercussionLookup($instrumentid) {
$begin = __LINE__;
/** This is not a comment!
35 Acoustic Bass Drum
36 Bass Drum 1
37 Side Stick
38 Acoustic Snare
39 Hand Clap
40 Electric Snare
41 Low Floor Tom
42 Closed Hi-Hat
43 High Floor Tom
44 Pedal Hi-Hat
45 Low Tom
46 Open Hi-Hat
47 Low-Mid Tom
48 Hi-Mid Tom
49 Crash Cymbal 1
50 High Tom
51 Ride Cymbal 1
52 Chinese Cymbal
53 Ride Bell
54 Tambourine
55 Splash Cymbal
56 Cowbell
57 Crash Cymbal 2
59 Ride Cymbal 2
60 Hi Bongo
61 Low Bongo
62 Mute Hi Conga
63 Open Hi Conga
64 Low Conga
65 High Timbale
66 Low Timbale
67 High Agogo
68 Low Agogo
69 Cabasa
70 Maracas
71 Short Whistle
72 Long Whistle
73 Short Guiro
74 Long Guiro
75 Claves
76 Hi Wood Block
77 Low Wood Block
78 Mute Cuica
79 Open Cuica
80 Mute Triangle
81 Open Triangle
*/
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
}
}
?>
+101
View File
@@ -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
{
// new combined constructor
function getid3_mod(&$fd, &$ThisFileInfo, $option) {
if ($option === 'mod') {
$this->getMODheaderFilepointer($fd, $ThisFileInfo);
}
elseif ($option === 'xm') {
$this->getXMheaderFilepointer($fd, $ThisFileInfo);
}
elseif ($option === 'it') {
$this->getITheaderFilepointer($fd, $ThisFileInfo);
}
elseif ($option === 's3m') {
$this->getS3MheaderFilepointer($fd, $ThisFileInfo);
}
}
function getMODheaderFilepointer(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'] + 1080);
$FormatID = fread($fd, 4);
if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) {
$ThisFileInfo['error'][] = 'This is not a known type of MOD file';
return false;
}
$ThisFileInfo['fileformat'] = 'mod';
$ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()';
return false;
}
function getXMheaderFilepointer(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset']);
$FormatID = fread($fd, 15);
if (!ereg('^Extended Module$', $FormatID)) {
$ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file';
return false;
}
$ThisFileInfo['fileformat'] = 'xm';
$ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()';
return false;
}
function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'] + 44);
$FormatID = fread($fd, 4);
if (!ereg('^SCRM$', $FormatID)) {
$ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file';
return false;
}
$ThisFileInfo['fileformat'] = 's3m';
$ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()';
return false;
}
function getITheaderFilepointer(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset']);
$FormatID = fread($fd, 4);
if (!ereg('^IMPM$', $FormatID)) {
$ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file';
return false;
}
$ThisFileInfo['fileformat'] = 'it';
$ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()';
return false;
}
}
?>
+202
View File
@@ -0,0 +1,202 @@
<?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.monkey.php //
// module for analyzing Monkey's Audio files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_monkey
{
function getid3_monkey(&$fd, &$ThisFileInfo) {
// 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;
$ThisFileInfo['monkeys_audio']['raw'] = array();
$thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio'];
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$MACheaderData = fread($fd, 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']);
return false;
}
$thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2));
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 2));
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4));
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 4));
$thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4));
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 4));
$thisfile_monkeysaudio_raw['nFinalFrameSamples'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4));
$thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4));
$thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2));
$offset = 8;
} else {
$offset = 8;
// APE_DESCRIPTOR
$thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
$offset += 16;
// APE_HEADER
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
$offset += 2;
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
$offset += 2;
$thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
$thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
$offset += 2;
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
$offset += 2;
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
$offset += 4;
}
$thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
$thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
$thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004);
$thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008);
$thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010);
$thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020);
$thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
$thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
$thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
}
$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'];
$thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
if ($thisfile_monkeysaudio['sample_rate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero';
return false;
}
$ThisFileInfo['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);
}
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
} else {
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
}
$thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
if ($thisfile_monkeysaudio['playtime'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero';
return false;
}
$ThisFileInfo['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
$thisfile_monkeysaudio['compressed_size'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['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';
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'];
// 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'];
$ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
} else {
$ThisFileInfo['avdataoffset'] += $offset;
}
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
//$ThisFileInfo['warning'][] = 'cFileMD5 is null';
} else {
$ThisFileInfo['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);
}
if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
unset($ThisFileInfo['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';
return true;
}
function MonkeyCompressionLevelNameLookup($compressionlevel) {
static $MonkeyCompressionLevelNameLookup = array(
0 => 'unknown',
1000 => 'fast',
2000 => 'normal',
3000 => 'high',
4000 => 'extra-high',
5000 => 'insane'
);
return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
}
function MonkeySamplesPerFrame($versionid, $compressionlevel) {
if ($versionid >= 3950) {
return 73728 * 4;
} elseif ($versionid >= 3900) {
return 73728;
} elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
return 73728;
} else {
return 9216;
}
}
}
?>
File diff suppressed because it is too large Load Diff
+502
View File
@@ -0,0 +1,502 @@
<?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.mpc.php //
// module for analyzing Musepack/MPEG+ Audio files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_mpc
{
function getid3_mpc(&$fd, &$ThisFileInfo) {
$ThisFileInfo['mpc']['header'] = array();
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
$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;
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'])) {
// this is SV8
return $this->ParseMPCsv8($fd, $ThisFileInfo);
} elseif (ereg('^MP\+', $ThisFileInfo['mpc']['header']['preamble'])) {
// this is SV7
return $this->ParseMPCsv7($fd, $ThisFileInfo);
} 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);
} else {
$ThisFileInfo['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, 0, 4).'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['mpc']);
return false;
}
return false;
}
function ParseMPCsv8(&$fd, &$ThisFileInfo) {
// this is SV8
// http://trac.musepack.net/trac/wiki/SV8Specification
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
$keyNameSize = 2;
$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
$offset = ftell($fd);
while ($offset < $ThisFileInfo['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);
$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'];
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);
return false;
}
$packet_offset += $packetLength;
$offset += $thisPacket['packet_size'];
switch ($thisPacket['key']) {
case 'SH': // Stream Header
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($fd, $moreBytesToRead);
}
$thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
$packet_offset += 4;
$thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1;
$packetLength = 0;
$thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
$packet_offset += $packetLength;
$packetLength = 0;
$thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
$packet_offset += $packetLength;
$otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
$packet_offset += 2;
$thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
$thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
$thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
$thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
$thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
$thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
$thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
$thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
$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'];
break;
case 'RG': // Replay Gain
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($fd, $moreBytesToRead);
}
$thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1;
$thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
$packet_offset += 2;
$thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
$packet_offset += 2;
$thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
$packet_offset += 2;
$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']; }
break;
case 'EI': // Encoder Info
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($fd, $moreBytesToRead);
}
$profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1;
$quality_int = (($profile_pns & 0xF0) >> 4);
$quality_dec = (($profile_pns & 0x0E) >> 3);
$thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
$thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
$thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1;
$thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1;
$thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$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'];
//$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;
case 'SO': // Seek Table Offset
$packetLength = 0;
$thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
$packet_offset += $packetLength;
break;
case 'ST': // Seek Table
case 'SE': // Stream End
case 'AP': // Audio Data
// nothing useful here, just skip this packet
$thisPacket = array();
break;
default:
$ThisFileInfo['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
return false;
break;
}
if (!empty($thisPacket)) {
$ThisFileInfo['mpc']['packets'][] = $thisPacket;
}
fseek($fd, $offset);
}
$thisfile_mpc_header['size'] = $offset;
return true;
}
function ParseMPCsv7(&$fd, &$ThisFileInfo) {
// this is SV7
// http://www.uni-jena.de/~pfk/mpp/sv8/header.html
$thisfile_mpc_header = &$ThisFileInfo['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']));
$offset = strlen('MP+');
$StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
$offset += 1;
$thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
$thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
$thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
$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'].')';
return false;
}
$FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
$offset += 4;
$thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
$thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
$thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
$thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
$thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
$thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
$thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
$thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
$offset += 2;
$thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
$offset += 2;
$thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
$offset += 2;
$thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
$offset += 2;
$FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
$offset += 4;
$thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
$thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
$thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
$offset += 3;
$thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
$offset += 1;
$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';
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'];
$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';
return false;
}
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
$ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['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']);
if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
$thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
} else {
$thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
}
$thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak'];
$thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
$thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
} else {
$thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
}
$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'];
if ($thisfile_mpc_header['title_peak'] > 0) {
$ThisFileInfo['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
}
if ($thisfile_mpc_header['album_peak'] > 0) {
$ThisFileInfo['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'];
$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) {
// this is SV4 - SV6
$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
$offset = 0;
$thisfile_mpc_header['size'] = 8;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$MPCheaderData = fread($fd, $thisfile_mpc_header['size']);
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
$ThisFileInfo['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));
$HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
// DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
// aaaa aaaa abcd dddd dddd deee eeff ffff
//
// a = bitrate = anything
// b = IS = anything
// c = MS = anything
// d = streamversion = 0000000004 or 0000000005 or 0000000006
// e = maxband = anything
// f = blocksize = 000001 for SV5+, anything(?) for SV4
$thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
$thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
$thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
$thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
$thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
$thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
switch ($thisfile_mpc_header['stream_version_major']) {
case 4:
$thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
break;
case 5:
case 6:
$thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
break;
default:
$ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
unset($ThisFileInfo['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'];
}
$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'];
if ($thisfile_mpc_header['target_bitrate'] == 0) {
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
} else {
$ThisFileInfo['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'];
return true;
}
function MPCprofileNameLookup($profileid) {
static $MPCprofileNameLookup = array(
0 => 'no profile',
1 => 'Experimental',
2 => 'unused',
3 => 'unused',
4 => 'unused',
5 => 'below Telephone (q = 0.0)',
6 => 'below Telephone (q = 1.0)',
7 => 'Telephone (q = 2.0)',
8 => 'Thumb (q = 3.0)',
9 => 'Radio (q = 4.0)',
10 => 'Standard (q = 5.0)',
11 => 'Extreme (q = 6.0)',
12 => 'Insane (q = 7.0)',
13 => 'BrainDead (q = 8.0)',
14 => 'above BrainDead (q = 9.0)',
15 => 'above BrainDead (q = 10.0)'
);
return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
}
function MPCfrequencyLookup($frequencyid) {
static $MPCfrequencyLookup = array(
0 => 44100,
1 => 48000,
2 => 37800,
3 => 32000
);
return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
}
function MPCpeakDBLookup($intvalue) {
if ($intvalue > 0) {
return ((log10($intvalue) / log10(2)) - 15) * 6;
}
return false;
}
function MPCencoderVersionLookup($encoderversion) {
//Encoder version * 100 (106 = 1.06)
//EncoderVersion % 10 == 0 Release (1.0)
//EncoderVersion % 2 == 0 Beta (1.06)
//EncoderVersion % 2 == 1 Alpha (1.05a...z)
if ($encoderversion == 0) {
// very old version, not known exactly which
return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
}
if (($encoderversion % 10) == 0) {
// release version
return number_format($encoderversion / 100, 2);
} elseif (($encoderversion % 2) == 0) {
// beta version
return number_format($encoderversion / 100, 2).' beta';
}
// alpha version
return number_format($encoderversion / 100, 2).' alpha';
}
function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
$packet_size = 0;
for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
// variable-length size field:
// bits, big-endian
// 0xxx xxxx - value 0 to 2^7-1
// 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
// 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
// 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
// ...
$thisbyte = ord(substr($data, ($packetLength - 1), 1));
// look through bytes until find a byte with MSB==0
$packet_size = ($packet_size << 7);
$packet_size = ($packet_size | ($thisbyte & 0x7F));
if (($thisbyte & 0x80) === 0) {
break;
}
if ($packetLength >= $maxHandledPacketLength) {
return false;
}
}
return $packet_size;
}
function MPCsv8PacketName($packetKey) {
static $MPCsv8PacketName = array();
if (empty($MPCsv8PacketName)) {
$MPCsv8PacketName = array(
'AP' => 'Audio Packet',
'CT' => 'Chapter Tag',
'EI' => 'Encoder Info',
'RG' => 'Replay Gain',
'SE' => 'Stream End',
'SH' => 'Stream Header',
'SO' => 'Seek Table Offset',
'ST' => 'Seek Table',
);
}
return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
}
}
?>
+556
View File
@@ -0,0 +1,556 @@
<?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
{
function getid3_ogg(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'ogg';
// Warn about illegal tags - only vorbiscomments are allowed
if (isset($ThisFileInfo['id3v2'])) {
$ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
}
if (isset($ThisFileInfo['id3v1'])) {
$ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
}
if (isset($ThisFileInfo['ape'])) {
$ThisFileInfo['warning'][] = 'Illegal APE tag present.';
}
// Page 1 - Stream Header
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
$ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['ogg']);
return false;
}
$filedata = fread($fd, $oggpageinfo['page_length']);
$filedataoffset = 0;
if (substr($filedata, 0, 4) == 'fLaC') {
$ThisFileInfo['audio']['dataformat'] = 'flac';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['audio']['lossless'] = true;
} elseif (substr($filedata, 1, 6) == 'vorbis') {
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
} elseif (substr($filedata, 0, 8) == 'Speex ') {
// http://www.speex.org/manual/node10.html
$ThisFileInfo['audio']['dataformat'] = 'speex';
$ThisFileInfo['mime_type'] = 'audio/speex';
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
$filedataoffset += 8;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
$filedataoffset += 20;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
$ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
$ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
$ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
$ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels'];
if ($ThisFileInfo['speex']['vbr']) {
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
}
} else {
$ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither';
unset($ThisFileInfo['ogg']);
unset($ThisFileInfo['mime_type']);
return false;
}
// Page 2 - Comment Header
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
switch ($ThisFileInfo['audio']['dataformat']) {
case 'vorbis':
$filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
break;
case 'flac':
if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
$ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
return false;
}
break;
case 'speex':
fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
break;
}
// Last Page - Number of Samples
if ($ThisFileInfo['avdataend'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond 2GB)';
} else {
fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
$LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
$ThisFileInfo['avdataend'] = ftell($fd);
$ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
if ($ThisFileInfo['ogg']['samples'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
return false;
}
$ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
}
}
if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
} elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
} elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
}
if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
if ($ThisFileInfo['audio']['bitrate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
return false;
}
$ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
}
if (isset($ThisFileInfo['ogg']['vendor'])) {
$ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
// Vorbis only
if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
// Vorbis 1.0 starts with Xiph.Org
if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
// Set -b 128 on abr files
$ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
} elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
// Set -q N on vbr files
$ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
}
}
if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
$ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
}
}
}
return true;
}
function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) {
$ThisFileInfo['audio']['dataformat'] = 'vorbis';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
$filedataoffset += 6;
$ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels'];
$ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
if ($ThisFileInfo['ogg']['samplerate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
return false;
}
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate'];
$ThisFileInfo['ogg']['samples'] = 0; // filled in later
$ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later
$ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
$ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
$ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
unset($ThisFileInfo['ogg']['bitrate_max']);
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
}
if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
unset($ThisFileInfo['ogg']['bitrate_nominal']);
}
if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
unset($ThisFileInfo['ogg']['bitrate_min']);
$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
}
return true;
}
function ParseOggPageHeader(&$fd) {
// http://xiph.org/ogg/vorbis/doc/framing.html
$oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
$filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
$filedataoffset = 0;
while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
// should be found before here
return false;
}
if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
// 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($fd, $oggheader['header_end_offset'], SEEK_SET);
return $oggheader;
}
function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) {
$OriginalOffset = ftell($fd);
$CommentStartOffset = $OriginalOffset;
$commentdataoffset = 0;
$VorbisCommentPage = 1;
switch ($ThisFileInfo['audio']['dataformat']) {
case 'vorbis':
$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
fseek($fd, $CommentStartOffset, SEEK_SET);
$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
$commentdataoffset += (strlen('vorbis') + 1);
break;
case 'flac':
fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
$commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
break;
case 'speex':
$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
fseek($fd, $CommentStartOffset, SEEK_SET);
$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
break;
default:
return false;
break;
}
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
$commentdataoffset += 4;
$ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
$commentdataoffset += $VendorSize;
$CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
$commentdataoffset += 4;
$ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
$basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
for ($i = 0; $i < $CommentsCount; $i++) {
$ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
$VorbisCommentPage++;
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['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 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
// Finally, stick the unused data back on the end
$commentdata .= $AsYetUnusedData;
//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
}
$ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
// replace avdataoffset with position just after the last vorbiscomment
$ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
$commentdataoffset += 4;
while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
$ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
break 2;
}
$VorbisCommentPage++;
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
// 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 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
// Finally, stick the unused data back on the end
$commentdata .= $AsYetUnusedData;
//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
//$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
}
$commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
$commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
if (!$commentstring) {
// no comment?
$ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
} elseif (strstr($commentstring, '=')) {
$commentexploded = explode('=', $commentstring, 2);
$ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]);
$ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1];
$ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
$ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data'], $imageinfo);
$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::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]['image_mime']);
unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
}
} else {
$ThisFileInfo['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($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
switch ($index) {
case 'rg_audiophile':
case 'replaygain_album_gain':
$ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
unset($ThisFileInfo['ogg']['comments'][$index]);
break;
case 'rg_radio':
case 'replaygain_track_gain':
$ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
unset($ThisFileInfo['ogg']['comments'][$index]);
break;
case 'replaygain_album_peak':
$ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
unset($ThisFileInfo['ogg']['comments'][$index]);
break;
case 'rg_peak':
case 'replaygain_track_peak':
$ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
unset($ThisFileInfo['ogg']['comments'][$index]);
break;
default:
// do nothing
break;
}
}
}
fseek($fd, $OriginalOffset, SEEK_SET);
return true;
}
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);
}
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;
}
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
}
}
?>
+408
View File
@@ -0,0 +1,408 @@
<?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.optimfrog.php //
// module for analyzing OptimFROG audio files //
// dependencies: module.audio.riff.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_optimfrog
{
function getid3_optimfrog(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'ofr';
$ThisFileInfo['audio']['dataformat'] = 'ofr';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['audio']['lossless'] = true;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$OFRheader = fread($fd, 8);
if (substr($OFRheader, 0, 5) == '*RIFF') {
return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
} elseif (substr($OFRheader, 0, 3) == 'OFR') {
return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
}
$ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
unset($ThisFileInfo['fileformat']);
return false;
}
function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
// for fileformat of v4.21 and older
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$OptimFROGheaderData = fread($fd, 45);
$ThisFileInfo['avdataoffset'] = 45;
$OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
$OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
$RIFFdata = substr($OptimFROGheaderData, 1, 44);
$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
$OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
$ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
$RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
}
// 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'];
return true;
}
function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
// for fileformat of v4.50a and higher
$RIFFdata = '';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
$BlockOffset = ftell($fd);
$BlockData = fread($fd, 8);
$offset = 8;
$BlockName = substr($BlockData, 0, 4);
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
if ($BlockName == 'OFRX') {
$BlockName = 'OFR ';
}
if (!isset($ThisFileInfo['ofr'][$BlockName])) {
$ThisFileInfo['ofr'][$BlockName] = array();
}
$thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
switch ($BlockName) {
case 'OFR ':
// shortcut
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
$ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
switch ($BlockSize) {
case 12:
case 15:
// good
break;
default:
$ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
break;
}
$BlockData .= fread($fd, $BlockSize);
$thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
$offset += 6;
$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
$offset += 1;
$thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
$offset += 1;
$thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
$offset += 4;
if ($BlockSize > 12) {
// OFR 4.504b or higher
$thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
$thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
$thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
$offset += 2;
$thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
$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'];
if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
if (strtolower(getid3_lib::fileextension($ThisFileInfo['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;
}
}
}
$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']);
break;
case 'COMP':
// unlike other block types, there CAN be multiple COMP blocks
$COMPdata['offset'] = $BlockOffset;
$COMPdata['size'] = $BlockSize;
if ($ThisFileInfo['avdataoffset'] == 0) {
$ThisFileInfo['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);
$COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
$offset += 4;
$COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
$offset += 4;
$COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
$offset += 1;
$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
$offset += 1;
$COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
//$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
$offset += 2;
if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
// OFR 4.504b or higher
$COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
$COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
$offset += 2;
}
if ($COMPdata['crc_32'] == 0x454E4F4E) {
// ASCII value of 'NONE' - placeholder value in v4.50a
$COMPdata['crc_32'] = false;
}
$thisfile_ofr_thisblock[] = $COMPdata;
break;
case 'HEAD':
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
$RIFFdata .= fread($fd, $BlockSize);
break;
case 'TAIL':
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
if ($BlockSize > 0) {
$RIFFdata .= fread($fd, $BlockSize);
}
break;
case 'RECV':
// block contains no useful meta data - simply note and skip
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
fseek($fd, $BlockSize, SEEK_CUR);
break;
case 'APET':
// APEtag v2
$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()';
fseek($fd, $BlockSize, SEEK_CUR);
break;
case 'MD5 ':
// APEtag v2
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
if ($BlockSize == 16) {
$thisfile_ofr_thisblock['md5_binary'] = fread($fd, $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'];
} else {
$ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
fseek($fd, $BlockSize, SEEK_CUR);
}
break;
default:
$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);
break;
}
}
if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
$ThisFileInfo['avdataend'] = $ThisFileInfo['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'];
// 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);
return true;
}
function OptimFROGsampleTypeLookup($SampleType) {
static $OptimFROGsampleTypeLookup = array(
0 => 'unsigned int (8-bit)',
1 => 'signed int (8-bit)',
2 => 'unsigned int (16-bit)',
3 => 'signed int (16-bit)',
4 => 'unsigned int (24-bit)',
5 => 'signed int (24-bit)',
6 => 'unsigned int (32-bit)',
7 => 'signed int (32-bit)',
8 => 'float 0.24 (32-bit)',
9 => 'float 16.8 (32-bit)',
10 => 'float 24.0 (32-bit)'
);
return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
}
function OptimFROGbitsPerSampleTypeLookup($SampleType) {
static $OptimFROGbitsPerSampleTypeLookup = array(
0 => 8,
1 => 8,
2 => 16,
3 => 16,
4 => 24,
5 => 24,
6 => 32,
7 => 32,
8 => 32,
9 => 32,
10 => 32
);
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
}
function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
static $OptimFROGchannelConfigurationLookup = array(
0 => 'mono',
1 => 'stereo'
);
return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
}
function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
static $OptimFROGchannelConfigNumChannelsLookup = array(
0 => 1,
1 => 2
);
return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
}
// function OptimFROGalgorithmNameLookup($AlgorithID) {
// static $OptimFROGalgorithmNameLookup = array();
// return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
// }
function OptimFROGencoderNameLookup($EncoderID) {
// version = (encoderID >> 4) + 4500
// system = encoderID & 0xF
$EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
$EncoderSystemID = ($EncoderID & 0x0F);
static $OptimFROGencoderSystemLookup = array(
0x00 => 'Windows console',
0x01 => 'Linux console',
0x0F => 'unknown'
);
return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
}
function OptimFROGcompressionLookup($CompressionID) {
// mode = compression >> 3
// speedup = compression & 0x07
$CompressionModeID = ($CompressionID & 0xF8) >> 3;
//$CompressionSpeedupID = ($CompressionID & 0x07);
static $OptimFROGencoderModeLookup = array(
0x00 => 'fast',
0x01 => 'normal',
0x02 => 'high',
0x03 => 'extra', // extranew (some versions)
0x04 => 'best', // bestnew (some versions)
0x05 => 'ultra',
0x06 => 'insane',
0x07 => 'highnew',
0x08 => 'extranew',
0x09 => 'bestnew'
);
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
}
function OptimFROGspeedupLookup($CompressionID) {
// mode = compression >> 3
// speedup = compression & 0x07
//$CompressionModeID = ($CompressionID & 0xF8) >> 3;
$CompressionSpeedupID = ($CompressionID & 0x07);
static $OptimFROGencoderSpeedupLookup = array(
0x00 => '1x',
0x01 => '2x',
0x02 => '4x'
);
return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
}
}
?>
+92
View File
@@ -0,0 +1,92 @@
<?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
{
function getid3_rkau(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$RKAUHeader = fread($fd, 20);
if (substr($RKAUHeader, 0, 3) != 'RKA') {
$ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"';
return false;
}
$ThisFileInfo['fileformat'] = 'rkau';
$ThisFileInfo['audio']['dataformat'] = 'rkau';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
$ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) {
$ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')';
unset($ThisFileInfo['rkau']);
return false;
}
$ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
$ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
$ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
$ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
$ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
$this->RKAUqualityLookup($ThisFileInfo['rkau']);
$ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
$ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01));
$ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02);
$ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04);
if ($ThisFileInfo['rkau']['flags']['streaming']) {
$ThisFileInfo['avdataoffset'] += 20;
$ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
} else {
$ThisFileInfo['avdataoffset'] += 16;
$ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1;
}
// Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
// sometimes it's more, sometimes less. No idea why(?)
$ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample'];
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8));
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds'];
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;
}
}
?>
+180
View File
@@ -0,0 +1,180 @@
<?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_shorten
{
function getid3_shorten(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['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).'"';
return false;
}
$ThisFileInfo['fileformat'] = 'shn';
$ThisFileInfo['audio']['dataformat'] = 'shn';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET);
$SeekTableSignatureTest = fread($fd, 12);
$ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
if ($ThisFileInfo['shn']['seektable']['present']) {
$ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
$ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length'];
fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET);
$SeekTableMagic = fread($fd, 4);
if ($SeekTableMagic != 'SEEK') {
$ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"';
return false;
} else {
// typedef struct tag_TSeekEntry
// {
// unsigned long SampleNumber;
// unsigned long SHNFileByteOffset;
// unsigned long SHNLastBufferReadPosition;
// unsigned short SHNByteGet;
// unsigned short SHNBufferOffset;
// unsigned short SHNFileBitOffset;
// unsigned long SHNGBuffer;
// unsigned short SHNBitShift;
// long CBuf0[3];
// long CBuf1[3];
// long Offset0[4];
// 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();
//$SeekTableOffset = 0;
//for ($i = 0; $i < $ThisFileInfo['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));
// $SeekTableOffset += 4;
// $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
// $SeekTableOffset += 2;
// $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
// $SeekTableOffset += 2;
// $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
// $SeekTableOffset += 2;
// $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
// $SeekTableOffset += 2;
// for ($j = 0; $j < 3; $j++) {
// $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// }
// for ($j = 0; $j < 3; $j++) {
// $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// }
// for ($j = 0; $j < 4; $j++) {
// $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// }
// for ($j = 0; $j < 4; $j++) {
// $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
// $SeekTableOffset += 4;
// }
//
// $ThisFileInfo['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';
return false;
}
if (GETID3_OS_ISWINDOWS) {
$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';
return false;
}
}
$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['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';
}
$output = `$commandline`;
if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$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'];
if (substr($output, 20 + $fmt_size, 4) == 'data') {
$ThisFileInfo['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';
return false;
}
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8;
} else {
$ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing';
return false;
}
return true;
}
}
?>
+107
View File
@@ -0,0 +1,107 @@
<?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
{
function getid3_tta(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'tta';
$ThisFileInfo['audio']['dataformat'] = 'tta';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$ttaheader = fread($fd, 26);
$ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3);
if ($ThisFileInfo['tta']['magic'] != 'TTA') {
$ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['audio']);
unset($ThisFileInfo['tta']);
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."
$ThisFileInfo['tta']['major_version'] = 1;
$ThisFileInfo['avdataoffset'] += 16;
$ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3});
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
$ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
$ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate'];
break;
case '2': // TTA v2.x
// "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
$ThisFileInfo['tta']['major_version'] = 2;
$ThisFileInfo['avdataoffset'] += 20;
$ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
$ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
$ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
$ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
break;
case '1': // TTA v3.x
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
$ThisFileInfo['tta']['major_version'] = 3;
$ThisFileInfo['avdataoffset'] += 26;
$ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup()
$ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
$ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
$ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
$ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
$ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
$ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
break;
default:
$ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3};
return false;
break;
}
$ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version'];
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample'];
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate'];
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels'];
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
return true;
}
}
?>
+205
View File
@@ -0,0 +1,205 @@
<?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.voc.php //
// module for analyzing Creative VOC Audio files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_voc
{
function getid3_voc(&$fd, &$ThisFileInfo) {
$OriginalAVdataOffset = $ThisFileInfo['avdataoffset'];
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$VOCheader = fread($fd, 26);
if (substr($VOCheader, 0, 19) != 'Creative Voice File') {
$ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"';
return false;
}
// shortcuts
$thisfile_audio = &$ThisFileInfo['audio'];
$ThisFileInfo['voc'] = array();
$thisfile_voc = &$ThisFileInfo['voc'];
$ThisFileInfo['fileformat'] = 'voc';
$thisfile_audio['dataformat'] = 'voc';
$thisfile_audio['bitrate_mode'] = 'cbr';
$thisfile_audio['lossless'] = true;
$thisfile_audio['channels'] = 1; // might be overriden below
$thisfile_audio['bits_per_sample'] = 8; // might be overriden below
// byte # Description
// ------ ------------------------------------------
// 00-12 'Creative Voice File'
// 13 1A (eof to abort printing of file)
// 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
// 16-17 Version number (minor,major) (VOC-HDR puts 0A 01)
// 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
$thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
$thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
$thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
do {
$BlockOffset = ftell($fd);
$BlockData = fread($fd, 4);
$BlockType = ord($BlockData{0});
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
$ThisBlock = array();
@$thisfile_voc['blocktypes'][$BlockType]++;
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);
}
fseek($fd, $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));
$ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
if ($ThisBlock['compression_type'] <= 3) {
$thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
}
// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
if (empty($thisfile_audio['sample_rate'])) {
// SR byte = 256 - (1000000 / sample_rate)
$thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
}
break;
case 2: // Sound continue
case 3: // Silence
case 4: // Marker
case 6: // Repeat
case 7: // End repeat
// nothing useful, just skip
fseek($fd, $BlockSize, SEEK_CUR);
break;
case 8: // Extended
$BlockData .= fread($fd, 4);
//00-01 Time Constant:
// Mono: 65536 - (256000000 / sample_rate)
// Stereo: 65536 - (256000000 / (sample_rate * 2))
$ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
$ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
$ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
$thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
$thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
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);
}
fseek($fd, $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));
$ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
$ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
$ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
$thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
}
$thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
$thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
$thisfile_audio['channels'] = $ThisBlock['channels'];
break;
default:
$ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
fseek($fd, $BlockSize, SEEK_CUR);
break;
}
if (!empty($ThisBlock)) {
$ThisBlock['block_offset'] = $BlockOffset;
$ThisBlock['block_size'] = $BlockSize;
$ThisBlock['block_type_id'] = $BlockType;
$thisfile_voc['blocks'][] = $ThisBlock;
}
} while (!feof($fd) && ($BlockType != 0));
// Terminator block doesn't have size field, so seek back 3 spaces
fseek($fd, -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'];
}
return true;
}
function VOCcompressionTypeLookup($index) {
static $VOCcompressionTypeLookup = array(
0 => '8-bit',
1 => '4-bit',
2 => '2.6-bit',
3 => '2-bit'
);
return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
}
function VOCwFormatLookup($index) {
static $VOCwFormatLookup = array(
0x0000 => '8-bit unsigned PCM',
0x0001 => 'Creative 8-bit to 4-bit ADPCM',
0x0002 => 'Creative 8-bit to 3-bit ADPCM',
0x0003 => 'Creative 8-bit to 2-bit ADPCM',
0x0004 => '16-bit signed PCM',
0x0006 => 'CCITT a-Law',
0x0007 => 'CCITT u-Law',
0x2000 => 'Creative 16-bit to 4-bit ADPCM'
);
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
}
function VOCwFormatActualBitsPerSampleLookup($index) {
static $VOCwFormatLookup = array(
0x0000 => 8,
0x0001 => 4,
0x0002 => 3,
0x0003 => 2,
0x0004 => 16,
0x0006 => 8,
0x0007 => 8,
0x2000 => 4
);
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
}
}
?>
+159
View File
@@ -0,0 +1,159 @@
<?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.vqf.php //
// module for analyzing VQF audio files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_vqf
{
function getid3_vqf(&$fd, &$ThisFileInfo) {
// 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;
// shortcut
$ThisFileInfo['vqf']['raw'] = array();
$thisfile_vqf = &$ThisFileInfo['vqf'];
$thisfile_vqf_raw = &$thisfile_vqf['raw'];
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$VQFheaderData = fread($fd, 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']);
return false;
}
$offset += 4;
$thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
$offset += 8;
$thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
$offset += 4;
while (ftell($fd) < $ThisFileInfo['avdataend']) {
$ChunkBaseOffset = ftell($fd);
$chunkoffset = 0;
$ChunkData = fread($fd, 8);
$ChunkName = substr($ChunkData, $chunkoffset, 4);
if ($ChunkName == 'DATA') {
$ThisFileInfo['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;
break;
}
if ($ChunkSize > 0) {
$ChunkData .= fread($fd, $ChunkSize);
}
switch ($ChunkName) {
case 'COMM':
// shortcut
$thisfile_vqf['COMM'] = array();
$thisfile_vqf_COMM = &$thisfile_vqf['COMM'];
$thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
$chunkoffset += 4;
$thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
$chunkoffset += 4;
$thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
$chunkoffset += 4;
$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);
if ($ThisFileInfo['audio']['bitrate'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
return false;
}
break;
case 'NAME':
case 'AUTH':
case '(c) ':
case 'FILE':
case 'COMT':
case 'ALBM':
$thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
break;
case 'DSIZ':
$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
break;
default:
$ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
break;
}
}
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['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';
break;
default:
$ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'));
break;
}
}
return true;
}
function VQFchannelFrequencyLookup($frequencyid) {
static $VQFchannelFrequencyLookup = array(
11 => 11025,
22 => 22050,
44 => 44100
);
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
}
function VQFcommentNiceNameLookup($shortname) {
static $VQFcommentNiceNameLookup = array(
'NAME' => 'title',
'AUTH' => 'artist',
'(c) ' => 'copyright',
'FILE' => 'filename',
'COMT' => 'comment',
'ALBM' => 'album'
);
return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
}
}
?>
+372
View File
@@ -0,0 +1,372 @@
<?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
{
function getid3_wavpack(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
while (true) {
$wavpackheader = fread($fd, 32);
if (ftell($fd) >= $ThisFileInfo['avdataend']) {
break;
} elseif (feof($fd)) {
break;
} elseif (
(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) &&
(@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) &&
(!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) &&
((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) {
break;
}
$blockheader_offset = ftell($fd) - 32;
$blockheader_magic = substr($wavpackheader, 0, 4);
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
if ($blockheader_magic != 'wvpk') {
$ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"';
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['audio']);
unset($ThisFileInfo['wavpack']);
}
return false;
}
if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) ||
(@$ThisFileInfo['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.
$ThisFileInfo['audio']['dataformat'] = 'wavpack';
$ThisFileInfo['fileformat'] = 'wavpack';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset;
$ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic;
$ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size;
if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) {
$ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['audio']);
unset($ThisFileInfo['wavpack']);
}
return false;
}
$ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
$ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) ||
(($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) &&
($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) {
$ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['audio']);
unset($ThisFileInfo['wavpack']);
}
return false;
}
$ThisFileInfo['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
$ThisFileInfo['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
$ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
$ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
$ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
$ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
$ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
$ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003);
$ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004);
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008);
$ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010);
$ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020);
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040);
$ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080);
$ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100);
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200);
$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400);
$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800);
$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000);
$ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'];
}
while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) {
$metablock = array('offset'=>ftell($fd));
$metablockheader = fread($fd, 2);
if (feof($fd)) {
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($fd, 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($fd, $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($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
break;
default:
$ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
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_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader);
$metablock['riff'] = $ParsedRIFFheader['riff'];
$metablock['riff']['original_filesize'] = $original_wav_filesize;
$ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
$ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec'];
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
// 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);
$ftell_old = ftell($fd);
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
$ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array());
$metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter);
fseek($fd, $ftell_old, SEEK_SET);
if (!empty($metablock['riff']['INFO'])) {
getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']);
$ThisFileInfo['tags']['riff'] = $metablock['comments'];
}
break;
case 0x23: // ID_REPLAY_GAIN
$ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
break;
case 0x24: // ID_CUESHEET
$ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
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 %
$ThisFileInfo['wavpack']['config_flags'] = $metablock['flags'];
if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) {
@$ThisFileInfo['audio']['encoder_options'] .= ' -b???';
}
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : '');
if (@$ThisFileInfo['audio']['encoder_options']) {
$ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']);
}
elseif (isset($ThisFileInfo['audio']['encoder_options'])) {
unset($ThisFileInfo['audio']['encoder_options']);
}
break;
case 0x26: // ID_MD5_CHECKSUM
if (strlen($metablock['data']) == 16) {
$ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
} else {
$ThisFileInfo['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)) {
$ThisFileInfo['wavpack']['metablocks'][] = $metablock;
}
}
}
$ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
$ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
if (@$ThisFileInfo['playtime_seconds']) {
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
} else {
$ThisFileInfo['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 (@$WavPackMetablockNameLookup[$id]);
}
}
?>
+685
View File
@@ -0,0 +1,685 @@
<?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.bmp.php //
// module for analyzing BMP Image files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_bmp
{
function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
// shortcuts
$ThisFileInfo['bmp']['header']['raw'] = array();
$thisfile_bmp = &$ThisFileInfo['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
// WORD bfType;
// DWORD bfSize;
// WORD bfReserved1;
// WORD bfReserved2;
// DWORD bfOffBits;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$offset = 0;
$BMPheader = fread($fd, 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']);
return false;
}
$thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
// check if the hardcoded-to-1 "planes" is at offset 22 or 26
$planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2));
$planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2));
if (($planes22 == 1) && ($planes26 != 1)) {
$thisfile_bmp['type_os'] = 'OS/2';
$thisfile_bmp['type_version'] = 1;
} elseif (($planes26 == 1) && ($planes22 != 1)) {
$thisfile_bmp['type_os'] = 'Windows';
$thisfile_bmp['type_version'] = 1;
} elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
$thisfile_bmp['type_os'] = 'OS/2';
$thisfile_bmp['type_version'] = 1;
} elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
$thisfile_bmp['type_os'] = 'Windows';
$thisfile_bmp['type_version'] = 1;
} elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
$thisfile_bmp['type_os'] = 'Windows';
$thisfile_bmp['type_version'] = 4;
} elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
$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']);
return false;
}
$ThisFileInfo['fileformat'] = 'bmp';
$ThisFileInfo['video']['dataformat'] = 'bmp';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
if ($thisfile_bmp['type_os'] == 'OS/2') {
// OS/2-format BMP
// http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
// DWORD Size; /* Size of this structure in bytes */
// DWORD Width; /* Bitmap width in pixels */
// DWORD Height; /* Bitmap height in pixel */
// WORD NumPlanes; /* Number of bit planes (color depth) */
// WORD BitsPerPixel; /* Number of bits per pixel per plane */
$thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$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'];
if ($thisfile_bmp['type_version'] >= 2) {
// DWORD Compression; /* Bitmap compression scheme */
// DWORD ImageDataSize; /* Size of bitmap data in bytes */
// DWORD XResolution; /* X resolution of display device */
// DWORD YResolution; /* Y resolution of display device */
// DWORD ColorsUsed; /* Number of color table indices used */
// DWORD ColorsImportant; /* Number of important color indices */
// WORD Units; /* Type of units used to measure resolution */
// WORD Reserved; /* Pad structure to 4-byte boundary */
// WORD Recording; /* Recording algorithm */
// WORD Rendering; /* Halftoning algorithm used */
// DWORD Size1; /* Reserved for halftoning algorithm use */
// DWORD Size2; /* Reserved for halftoning algorithm use */
// DWORD ColorEncoding; /* Color model used in bitmap */
// DWORD Identifier; /* Reserved for application use */
$thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['recording'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['rendering'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['size1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['size2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['color_encoding'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$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']);
$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
}
} elseif ($thisfile_bmp['type_os'] == 'Windows') {
// Windows-format BMP
// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
// all versions
// DWORD biSize;
// LONG biWidth;
// LONG biHeight;
// WORD biPlanes;
// WORD biBitCount;
// DWORD biCompression;
// DWORD biSizeImage;
// LONG biXPelsPerMeter;
// LONG biYPelsPerMeter;
// DWORD biClrUsed;
// DWORD biClrImportant;
// possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
$thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
$offset += 4;
$thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
$offset += 4;
$thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
$offset += 2;
$thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
$offset += 4;
$thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
$offset += 4;
$thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$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'];
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);
// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
// Win95+, WinNT4.0+
// DWORD bV4RedMask;
// DWORD bV4GreenMask;
// DWORD bV4BlueMask;
// DWORD bV4AlphaMask;
// DWORD bV4CSType;
// CIEXYZTRIPLE bV4Endpoints;
// DWORD bV4GammaRed;
// DWORD bV4GammaGreen;
// DWORD bV4GammaBlue;
$thisfile_bmp_header_raw['red_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['green_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['blue_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['alpha_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['cs_type'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
$offset += 4;
$thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
$offset += 4;
$thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
$offset += 4;
$thisfile_bmp_header_raw['gamma_red'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['gamma_green'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['gamma_blue'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header['ciexyz_red'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
$thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
$thisfile_bmp_header['ciexyz_blue'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
}
if ($thisfile_bmp['type_version'] >= 5) {
$BMPheader .= fread($fd, 16);
// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
// Win98+, Win2000+
// DWORD bV5Intent;
// DWORD bV5ProfileData;
// DWORD bV5ProfileSize;
// DWORD bV5Reserved;
$thisfile_bmp_header_raw['intent'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['profile_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
$thisfile_bmp_header_raw['reserved3'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$offset += 4;
}
} else {
$ThisFileInfo['error'][] = 'Unknown BMP format in header.';
return false;
}
if ($ExtractPalette || $ExtractData) {
$PaletteEntries = 0;
if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
$PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
} elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
$PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
}
if ($PaletteEntries > 0) {
$BMPpalette = fread($fd, 4 * $PaletteEntries);
$paletteoffset = 0;
for ($i = 0; $i < $PaletteEntries; $i++) {
// RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
// BYTE rgbBlue;
// BYTE rgbGreen;
// BYTE rgbRed;
// BYTE rgbReserved;
$blue = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
$green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
$red = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
// no padding byte
} else {
$paletteoffset++; // padding byte
}
$thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue);
}
}
}
if ($ExtractData) {
fseek($fd, $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);
$pixeldataoffset = 0;
switch (@$thisfile_bmp_header_raw['compression']) {
case 0: // BI_RGB
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
case 1:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
for ($i = 7; $i >= 0; $i--) {
$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
$col++;
}
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
case 4:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
for ($i = 1; $i >= 0; $i--) {
$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
$col++;
}
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
case 8:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
case 24:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
$pixeldataoffset += 3;
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
case 32:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
$pixeldataoffset += 4;
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
case 16:
// ?
break;
default:
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
break;
}
break;
case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
case 8:
$pixelcounter = 0;
while ($pixeldataoffset < strlen($BMPpixelData)) {
$firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
if ($firstbyte == 0) {
// escaped/absolute mode - the first byte of the pair can be set to zero to
// indicate an escape character that denotes the end of a line, the end of
// a bitmap, or a delta, depending on the value of the second byte.
switch ($secondbyte) {
case 0:
// end of line
// no need for special processing, just ignore
break;
case 1:
// end of bitmap
$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
break;
case 2:
// delta - The 2 bytes following the escape contain unsigned values
// indicating the horizontal and vertical offsets of the next pixel
// from the current position.
$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
break;
default:
// In absolute mode, the first byte is zero and the second byte is a
// value in the range 03H through FFH. The second byte represents the
// number of bytes that follow, each of which contains the color index
// of a single pixel. Each run must be aligned on a word boundary.
for ($i = 0; $i < $secondbyte; $i++) {
$paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
$pixelcounter++;
}
while (($pixeldataoffset % 2) != 0) {
// Each run must be aligned on a word boundary.
$pixeldataoffset++;
}
break;
}
} else {
// encoded mode - the first byte specifies the number of consecutive pixels
// to be drawn using the color index contained in the second byte.
for ($i = 0; $i < $firstbyte; $i++) {
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
$pixelcounter++;
}
}
}
break;
default:
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
break;
}
break;
case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
case 4:
$pixelcounter = 0;
while ($pixeldataoffset < strlen($BMPpixelData)) {
$firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
if ($firstbyte == 0) {
// escaped/absolute mode - the first byte of the pair can be set to zero to
// indicate an escape character that denotes the end of a line, the end of
// a bitmap, or a delta, depending on the value of the second byte.
switch ($secondbyte) {
case 0:
// end of line
// no need for special processing, just ignore
break;
case 1:
// end of bitmap
$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
break;
case 2:
// delta - The 2 bytes following the escape contain unsigned values
// indicating the horizontal and vertical offsets of the next pixel
// from the current position.
$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
break;
default:
// In absolute mode, the first byte is zero. The second byte contains the number
// of color indexes that follow. Subsequent bytes contain color indexes in their
// high- and low-order 4 bits, one color index for each pixel. In absolute mode,
// each run must be aligned on a word boundary.
unset($paletteindexes);
for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
$paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
$paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
$paletteindexes[] = ($paletteindexbyte & 0x0F);
}
while (($pixeldataoffset % 2) != 0) {
// Each run must be aligned on a word boundary.
$pixeldataoffset++;
}
foreach ($paletteindexes as $paletteindex) {
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
$pixelcounter++;
}
break;
}
} else {
// encoded mode - the first byte of the pair contains the number of pixels to be
// drawn using the color indexes in the second byte. The second byte contains two
// color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
// The first of the pixels is drawn using the color specified by the high-order
// 4 bits, the second is drawn using the color in the low-order 4 bits, the third
// is drawn using the color in the high-order 4 bits, and so on, until all the
// pixels specified by the first byte have been drawn.
$paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
$paletteindexes[1] = ($secondbyte & 0x0F);
for ($i = 0; $i < $firstbyte; $i++) {
$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
$pixelcounter++;
}
}
}
break;
default:
$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
break;
}
break;
case 3: // BI_BITFIELDS
switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
case 16:
case 32:
$redshift = 0;
$greenshift = 0;
$blueshift = 0;
while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
$redshift++;
}
while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
$greenshift++;
}
while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
$blueshift++;
}
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
$pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
$pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
$red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255));
$green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
$blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255));
$thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
}
while (($pixeldataoffset % 4) != 0) {
// lines are padded to nearest DWORD
$pixeldataoffset++;
}
}
break;
default:
$ThisFileInfo['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';
break;
}
}
return true;
}
function PlotBMP(&$BMPinfo) {
$starttime = time();
if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
echo 'ERROR: no pixel data<BR>';
return false;
}
set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) {
for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) {
for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) {
if (isset($BMPinfo['bmp']['data'][$row][$col])) {
$red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16;
$green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8;
$blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF);
$pixelcolor = ImageColorAllocate($im, $red, $green, $blue);
ImageSetPixel($im, $col, $row, $pixelcolor);
} else {
//echo 'ERROR: no data for pixel '.$row.' x '.$col.'<BR>';
//return false;
}
}
}
if (headers_sent()) {
echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
ImageDestroy($im);
exit;
} else {
header('Content-type: image/png');
ImagePNG($im);
ImageDestroy($im);
return true;
}
}
return false;
}
function BMPcompressionWindowsLookup($compressionid) {
static $BMPcompressionWindowsLookup = array(
0 => 'BI_RGB',
1 => 'BI_RLE8',
2 => 'BI_RLE4',
3 => 'BI_BITFIELDS',
4 => 'BI_JPEG',
5 => 'BI_PNG'
);
return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
}
function BMPcompressionOS2Lookup($compressionid) {
static $BMPcompressionOS2Lookup = array(
0 => 'BI_RGB',
1 => 'BI_RLE8',
2 => 'BI_RLE4',
3 => 'Huffman 1D',
4 => 'BI_RLE24',
);
return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
}
}
?>
+183
View File
@@ -0,0 +1,183 @@
<?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
{
function getid3_gif(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'gif';
$ThisFileInfo['video']['dataformat'] = 'gif';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$GIFheader = fread($fd, 13);
$offset = 0;
$ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
$offset += 3;
if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') {
$ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['gif']);
return false;
}
$ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
$offset += 3;
$ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
$offset += 2;
$ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
$offset += 2;
$ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
$offset += 1;
$ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
$offset += 1;
$ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
$offset += 1;
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width'];
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height'];
$ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version'];
$ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80);
if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) {
// Number of bits per primary color available to the original image, minus 1
$ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
} else {
$ThisFileInfo['gif']['header']['bits_per_pixel'] = 0;
}
$ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40);
if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
// the number of bytes contained in the Global Color Table. To determine that
// actual size of the color table, raise 2 to [the value of the field + 1]
$ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1);
$ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1;
} else {
$ThisFileInfo['gif']['header']['global_color_size'] = 0;
}
if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) {
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
$ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
}
// if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
// $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']);
// $offset = 0;
// for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) {
// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
// $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
// }
// }
//
// // Image Descriptor
// while (!feof($fd)) {
// $NextBlockTest = fread($fd, 1);
// switch ($NextBlockTest) {
//
// case ',': // ',' - Image separator character
//
// $ImageDescriptorData = $NextBlockTest.fread($fd, 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);
// $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor;
//
// if ($ImageDescriptor['flags']['use_local_color_map']) {
//
// $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
// return true;
//
// }
//echo 'Start of raster data: '.ftell($fd).'<BR>';
// $RasterData = array();
// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
// $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData;
//
// $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($fd, $CurrentCodeSize);
// echo 'Clear Code: '.$NextValue.'<BR>';
//
// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
// echo 'First Color: '.$NextValue.'<BR>';
//
// $Prefix = $NextValue;
//$i = 0;
// while ($i++ < 20) {
// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
// echo $NextValue.'<BR>';
// }
//return true;
// break;
//
// case '!':
// // GIF Extension Block
// $ExtensionBlockData = $NextBlockTest.fread($fd, 2);
// $ExtensionBlock = array();
// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
// $ExtensionBlock['data'] = fread($fd, $ExtensionBlock['byte_length']);
// $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock;
// break;
//
// case ';':
// $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1;
// // GIF Terminator
// break;
//
// default:
// break;
//
//
// }
// }
return true;
}
function GetLSBits($fd, $bits) {
static $bitbuffer = '';
while (strlen($bitbuffer) < $bits) {
//echo 'Read another byte: '.ftell($fd).'<BR>';
$bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
}
$value = bindec(substr($bitbuffer, 0 - $bits));
$bitbuffer = substr($bitbuffer, 0, 0 - $bits);
return $value;
}
}
?>
+249
View File
@@ -0,0 +1,249 @@
<?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: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_jpg
{
function getid3_jpg(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'jpg';
$ThisFileInfo['video']['dataformat'] = 'jpg';
$ThisFileInfo['video']['lossless'] = false;
$ThisFileInfo['video']['bits_per_sample'] = 24;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$imageinfo = array();
list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['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) {
@$ThisFileInfo['iptc'][$this->IPTCrecordName($iptc_record)][$this->IPTCrecordTagName($iptc_record, $iptc_tagkey)][] = $value;
}
}
}
//echo '<pre>'.htmlentities(print_r($iptc_parsed, true)).'</pre>';
}
switch ($type) {
case 2: // JPEG
$ThisFileInfo['video']['resolution_x'] = $width;
$ThisFileInfo['video']['resolution_y'] = $height;
if (version_compare(phpversion(), '4.2.0', '>=')) {
if (function_exists('exif_read_data')) {
ob_start();
$ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false);
$errors = ob_get_contents();
if ($errors) {
$ThisFileInfo['warning'][] = strip_tags($errors);
unset($ThisFileInfo['jpg']['exif']);
}
ob_end_clean();
} else {
$ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif');
}
} else {
$ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion();
}
return true;
break;
default:
break;
}
unset($ThisFileInfo['fileformat']);
return false;
}
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);
}
}
?>
+130
View File
@@ -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.graphic.pcd.php //
// module for analyzing PhotoCD (PCD) Image files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_pcd
{
function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) {
$ThisFileInfo['fileformat'] = 'pcd';
$ThisFileInfo['video']['dataformat'] = 'pcd';
$ThisFileInfo['video']['lossless'] = false;
fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET);
$PCDflags = fread($fd, 1);
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
if ($PCDisVertical) {
$ThisFileInfo['video']['resolution_x'] = 3072;
$ThisFileInfo['video']['resolution_y'] = 2048;
} else {
$ThisFileInfo['video']['resolution_x'] = 2048;
$ThisFileInfo['video']['resolution_y'] = 3072;
}
if ($ExtractData > 3) {
$ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)';
} elseif ($ExtractData > 0) {
$PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16
$PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4
$PCD_levels[3] = array( 768, 512, 0x30000); // BASE
//$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption
//$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption
//$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only
list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3];
fseek($fd, $ThisFileInfo['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.
// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
// consisting of w bytes, where w is the width of the image-subtype. The first w bytes and
// 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)));
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)}));
} 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)}));
}
}
}
// Example for plotting extracted data
//getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
//if ($PCDisVertical) {
// $BMPinfo['resolution_x'] = $PCD_height;
// $BMPinfo['resolution_y'] = $PCD_width;
//} else {
// $BMPinfo['resolution_x'] = $PCD_width;
// $BMPinfo['resolution_y'] = $PCD_height;
//}
//$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data'];
//getid3_bmp::PlotBMP($BMPinfo);
//exit;
}
}
function YCbCr2RGB($Y, $Cb, $Cr) {
static $YCbCr_constants = array();
if (empty($YCbCr_constants)) {
$YCbCr_constants['red']['Y'] = 0.0054980 * 256;
$YCbCr_constants['red']['Cb'] = 0.0000000 * 256;
$YCbCr_constants['red']['Cr'] = 0.0051681 * 256;
$YCbCr_constants['green']['Y'] = 0.0054980 * 256;
$YCbCr_constants['green']['Cb'] = -0.0015446 * 256;
$YCbCr_constants['green']['Cr'] = -0.0026325 * 256;
$YCbCr_constants['blue']['Y'] = 0.0054980 * 256;
$YCbCr_constants['blue']['Cb'] = 0.0079533 * 256;
$YCbCr_constants['blue']['Cr'] = 0.0000000 * 256;
}
$RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0);
foreach ($RGBcolor as $rgbname => $dummy) {
$RGBcolor[$rgbname] = max(0,
min(255,
intval(
round(
($YCbCr_constants[$rgbname]['Y'] * $Y) +
($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) +
($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137))
)
)
)
);
}
return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']);
}
}
?>
+519
View File
@@ -0,0 +1,519 @@
<?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.png.php //
// module for analyzing PNG Image files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_png
{
function getid3_png(&$fd, &$ThisFileInfo) {
// shortcut
$ThisFileInfo['png'] = array();
$thisfile_png = &$ThisFileInfo['png'];
$ThisFileInfo['fileformat'] = 'png';
$ThisFileInfo['video']['dataformat'] = 'png';
$ThisFileInfo['video']['lossless'] = false;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$PNGfiledata = fread($fd, 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']);
return false;
}
while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['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);
}
$chunk['type_text'] = substr($PNGfiledata, $offset, 4);
$offset += 4;
$chunk['type_raw'] = getid3_lib::BigEndian2Int($chunk['type_text']);
$chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']);
$offset += $chunk['data_length'];
$chunk['crc'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
$offset += 4;
$chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000);
$chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x00200000);
$chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x00002000);
$chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020);
// shortcut
$thisfile_png[$chunk['type_text']] = array();
$thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']];
switch ($chunk['type_text']) {
case 'IHDR': // Image Header
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['width'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4));
$thisfile_png_chunk_type_text['height'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4));
$thisfile_png_chunk_type_text['raw']['bit_depth'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
$thisfile_png_chunk_type_text['raw']['color_type'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 9, 1));
$thisfile_png_chunk_type_text['raw']['compression_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 10, 1));
$thisfile_png_chunk_type_text['raw']['filter_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 11, 1));
$thisfile_png_chunk_type_text['raw']['interlace_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 1));
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']);
$thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01);
$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'];
$ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']);
break;
case 'PLTE': // Palette
$thisfile_png_chunk_type_text['header'] = $chunk;
$paletteoffset = 0;
for ($i = 0; $i <= 255; $i++) {
//$thisfile_png_chunk_type_text['red'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
//$thisfile_png_chunk_type_text['green'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
//$thisfile_png_chunk_type_text['blue'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
$red = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
$green = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
$blue = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
$thisfile_png_chunk_type_text[$i] = (($red << 16) | ($green << 8) | ($blue));
}
break;
case 'tRNS': // Transparency
$thisfile_png_chunk_type_text['header'] = $chunk;
switch ($thisfile_png['IHDR']['raw']['color_type']) {
case 0:
$thisfile_png_chunk_type_text['transparent_color_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
break;
case 2:
$thisfile_png_chunk_type_text['transparent_color_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
$thisfile_png_chunk_type_text['transparent_color_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
$thisfile_png_chunk_type_text['transparent_color_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 2));
break;
case 3:
for ($i = 0; $i < strlen($chunk['data']); $i++) {
$thisfile_png_chunk_type_text['palette_opacity'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $i, 1));
}
break;
case 4:
case 6:
$ThisFileInfo['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'];
break;
}
break;
case 'gAMA': // Image Gamma
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['gamma'] = getid3_lib::BigEndian2Int($chunk['data']) / 100000;
break;
case 'cHRM': // Primary Chromaticities
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['white_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4)) / 100000;
$thisfile_png_chunk_type_text['white_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4)) / 100000;
$thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 4)) / 100000;
$thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 4)) / 100000;
$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 16, 4)) / 100000;
$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 20, 4)) / 100000;
$thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 24, 4)) / 100000;
$thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 28, 4)) / 100000;
break;
case 'sRGB': // Standard RGB Color Space
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['reindering_intent'] = getid3_lib::BigEndian2Int($chunk['data']);
$thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']);
break;
case 'iCCP': // Embedded ICC Profile
$thisfile_png_chunk_type_text['header'] = $chunk;
list($profilename, $compressiondata) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['profile_name'] = $profilename;
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1));
$thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1);
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
break;
case 'tEXt': // Textual Data
$thisfile_png_chunk_type_text['header'] = $chunk;
list($keyword, $text) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['keyword'] = $keyword;
$thisfile_png_chunk_type_text['text'] = $text;
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
break;
case 'zTXt': // Compressed Textual Data
$thisfile_png_chunk_type_text['header'] = $chunk;
list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['keyword'] = $keyword;
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
$thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1);
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
switch ($thisfile_png_chunk_type_text['compression_method']) {
case 0:
$thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']);
break;
default:
// unknown compression method
break;
}
if (isset($thisfile_png_chunk_type_text['text'])) {
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
}
break;
case 'iTXt': // International Textual Data
$thisfile_png_chunk_type_text['header'] = $chunk;
list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['keyword'] = $keyword;
$thisfile_png_chunk_type_text['compression'] = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
$thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1));
$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
list($languagetag, $translatedkeyword, $text) = explode("\x00", substr($otherdata, 2), 3);
$thisfile_png_chunk_type_text['language_tag'] = $languagetag;
$thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword;
if ($thisfile_png_chunk_type_text['compression']) {
switch ($thisfile_png_chunk_type_text['compression_method']) {
case 0:
$thisfile_png_chunk_type_text['text'] = gzuncompress($text);
break;
default:
// unknown compression method
break;
}
} else {
$thisfile_png_chunk_type_text['text'] = $text;
}
if (isset($thisfile_png_chunk_type_text['text'])) {
$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
}
break;
case 'bKGD': // Background Color
$thisfile_png_chunk_type_text['header'] = $chunk;
switch ($thisfile_png['IHDR']['raw']['color_type']) {
case 0:
case 4:
$thisfile_png_chunk_type_text['background_gray'] = getid3_lib::BigEndian2Int($chunk['data']);
break;
case 2:
case 6:
$thisfile_png_chunk_type_text['background_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
$thisfile_png_chunk_type_text['background_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
$thisfile_png_chunk_type_text['background_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
break;
case 3:
$thisfile_png_chunk_type_text['background_index'] = getid3_lib::BigEndian2Int($chunk['data']);
break;
default:
break;
}
break;
case 'pHYs': // Physical Pixel Dimensions
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['pixels_per_unit_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4));
$thisfile_png_chunk_type_text['pixels_per_unit_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4));
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
$thisfile_png_chunk_type_text['unit'] = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
break;
case 'sBIT': // Significant Bits
$thisfile_png_chunk_type_text['header'] = $chunk;
switch ($thisfile_png['IHDR']['raw']['color_type']) {
case 0:
$thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
break;
case 2:
case 3:
$thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
$thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
break;
case 4:
$thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
break;
case 6:
$thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
$thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
break;
default:
break;
}
break;
case 'sPLT': // Suggested Palette
$thisfile_png_chunk_type_text['header'] = $chunk;
list($palettename, $otherdata) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['palette_name'] = $palettename;
$sPLToffset = 0;
$thisfile_png_chunk_type_text['sample_depth_bits'] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1));
$sPLToffset += 1;
$thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8;
$paletteCounter = 0;
while ($sPLToffset < strlen($otherdata)) {
$thisfile_png_chunk_type_text['red'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
$thisfile_png_chunk_type_text['green'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
$thisfile_png_chunk_type_text['blue'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
$thisfile_png_chunk_type_text['alpha'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
$thisfile_png_chunk_type_text['frequency'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 2));
$sPLToffset += 2;
$paletteCounter++;
}
break;
case 'hIST': // Palette Histogram
$thisfile_png_chunk_type_text['header'] = $chunk;
$hISTcounter = 0;
while ($hISTcounter < strlen($chunk['data'])) {
$thisfile_png_chunk_type_text[$hISTcounter] = getid3_lib::BigEndian2Int(substr($chunk['data'], $hISTcounter / 2, 2));
$hISTcounter += 2;
}
break;
case 'tIME': // Image Last-Modification Time
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['year'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
$thisfile_png_chunk_type_text['month'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
$thisfile_png_chunk_type_text['day'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
$thisfile_png_chunk_type_text['hour'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 1));
$thisfile_png_chunk_type_text['minute'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 5, 1));
$thisfile_png_chunk_type_text['second'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 6, 1));
$thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']);
break;
case 'oFFs': // Image Offset
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['position_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4), false, true);
$thisfile_png_chunk_type_text['position_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4), false, true);
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
$thisfile_png_chunk_type_text['unit'] = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
break;
case 'pCAL': // Calibration Of Pixel Values
$thisfile_png_chunk_type_text['header'] = $chunk;
list($calibrationname, $otherdata) = explode("\x00", $chunk['data'], 2);
$thisfile_png_chunk_type_text['calibration_name'] = $calibrationname;
$pCALoffset = 0;
$thisfile_png_chunk_type_text['original_zero'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
$pCALoffset += 4;
$thisfile_png_chunk_type_text['original_max'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
$pCALoffset += 4;
$thisfile_png_chunk_type_text['equation_type'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
$pCALoffset += 1;
$thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']);
$thisfile_png_chunk_type_text['parameter_count'] = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
$pCALoffset += 1;
$thisfile_png_chunk_type_text['parameters'] = explode("\x00", substr($chunk['data'], $pCALoffset));
break;
case 'sCAL': // Physical Scale Of Image Subject
$thisfile_png_chunk_type_text['header'] = $chunk;
$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
$thisfile_png_chunk_type_text['unit'] = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
list($pixelwidth, $pixelheight) = explode("\x00", substr($chunk['data'], 1));
$thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth;
$thisfile_png_chunk_type_text['pixel_height'] = $pixelheight;
break;
case 'gIFg': // GIF Graphic Control Extension
$gIFgCounter = 0;
if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
$gIFgCounter = count($thisfile_png_chunk_type_text);
}
$thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk;
$thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
$thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
$thisfile_png_chunk_type_text[$gIFgCounter]['delay_time'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
break;
case 'gIFx': // GIF Application Extension
$gIFxCounter = 0;
if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
$gIFxCounter = count($thisfile_png_chunk_type_text);
}
$thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk;
$thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($chunk['data'], 0, 8);
$thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($chunk['data'], 8, 3);
$thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($chunk['data'], 11);
break;
case 'IDAT': // Image Data
$idatinformationfieldindex = 0;
if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) {
$idatinformationfieldindex = count($thisfile_png['IDAT']);
}
unset($chunk['data']);
$thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk;
break;
case 'IEND': // Image Trailer
$thisfile_png_chunk_type_text['header'] = $chunk;
break;
default:
//unset($chunk['data']);
$thisfile_png_chunk_type_text['header'] = $chunk;
$ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text'];
break;
}
}
return true;
}
function PNGsRGBintentLookup($sRGB) {
static $PNGsRGBintentLookup = array(
0 => 'Perceptual',
1 => 'Relative colorimetric',
2 => 'Saturation',
3 => 'Absolute colorimetric'
);
return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid');
}
function PNGcompressionMethodLookup($compressionmethod) {
static $PNGcompressionMethodLookup = array(
0 => 'deflate/inflate'
);
return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid');
}
function PNGpHYsUnitLookup($unitid) {
static $PNGpHYsUnitLookup = array(
0 => 'unknown',
1 => 'meter'
);
return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid');
}
function PNGoFFsUnitLookup($unitid) {
static $PNGoFFsUnitLookup = array(
0 => 'pixel',
1 => 'micrometer'
);
return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid');
}
function PNGpCALequationTypeLookup($equationtype) {
static $PNGpCALequationTypeLookup = array(
0 => 'Linear mapping',
1 => 'Base-e exponential mapping',
2 => 'Arbitrary-base exponential mapping',
3 => 'Hyperbolic mapping'
);
return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid');
}
function PNGsCALUnitLookup($unitid) {
static $PNGsCALUnitLookup = array(
0 => 'meter',
1 => 'radian'
);
return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid');
}
function IHDRcalculateBitsPerSample($color_type, $bit_depth) {
switch ($color_type) {
case 0: // Each pixel is a grayscale sample.
return $bit_depth;
break;
case 2: // Each pixel is an R,G,B triple
return 3 * $bit_depth;
break;
case 3: // Each pixel is a palette index; a PLTE chunk must appear.
return $bit_depth;
break;
case 4: // Each pixel is a grayscale sample, followed by an alpha sample.
return 2 * $bit_depth;
break;
case 6: // Each pixel is an R,G,B triple, followed by an alpha sample.
return 4 * $bit_depth;
break;
}
return false;
}
}
?>
+52
View File
@@ -0,0 +1,52 @@
<?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 //
// author: Bryce Harrington <bryceØbryceharrington*org> //
// ///
/////////////////////////////////////////////////////////////////
class getid3_svg
{
function getid3_svg(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
// I'm making this up, please modify as appropriate
$SVGheader = fread($fd, 32);
$ThisFileInfo['svg']['magic'] = substr($SVGheader, 0, 4);
if ($ThisFileInfo['svg']['magic'] == 'aBcD') {
$ThisFileInfo['fileformat'] = 'svg';
$ThisFileInfo['video']['dataformat'] = 'svg';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['video']['bits_per_sample'] = 24;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
$ThisFileInfo['svg']['width'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
$ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4));
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width'];
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height'];
return true;
}
$ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset'];
unset($ThisFileInfo['fileformat']);
return false;
}
}
?>
+221
View File
@@ -0,0 +1,221 @@
<?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.tiff.php //
// module for analyzing TIFF files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_tiff
{
function getid3_tiff(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$TIFFheader = fread($fd, 4);
switch (substr($TIFFheader, 0, 2)) {
case 'II':
$ThisFileInfo['tiff']['byte_order'] = 'Intel';
break;
case 'MM':
$ThisFileInfo['tiff']['byte_order'] = 'Motorola';
break;
default:
$ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset'];
return false;
break;
}
$ThisFileInfo['fileformat'] = 'tiff';
$ThisFileInfo['video']['dataformat'] = 'tiff';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['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']);
while ($nextIFDoffset > 0) {
$CurrentIFD['offset'] = $nextIFDoffset;
fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET);
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['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);
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']);
} else {
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
}
break;
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
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']);
}
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']);
} else {
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['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']);
} else {
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
}
break;
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
break;
}
}
$ThisFileInfo['tiff']['ifd'][] = $CurrentIFD;
$CurrentIFD = array();
$nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
}
foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) {
foreach ($IFDarray['fields'] as $key => $fieldarray) {
switch ($fieldarray['raw']['tag']) {
case 256: // ImageWidth
case 257: // ImageLength
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']]);
}
break;
case 270: // ImageDescription
case 271: // Make
case 272: // Model
case 305: // Software
case 306: // DateTime
case 315: // Artist
case 316: // HostComputer
if (isset($fieldarray['value'])) {
$ThisFileInfo['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']]);
}
break;
}
switch ($fieldarray['raw']['tag']) {
case 256: // ImageWidth
$ThisFileInfo['video']['resolution_x'] = $fieldarray['value'];
break;
case 257: // ImageLength
$ThisFileInfo['video']['resolution_y'] = $fieldarray['value'];
break;
case 258: // BitsPerSample
if (isset($fieldarray['value'])) {
$ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value'];
} else {
$ThisFileInfo['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']);
}
}
break;
case 259: // Compression
$ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
break;
case 270: // ImageDescription
case 271: // Make
case 272: // Model
case 305: // Software
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'];
break;
default:
break;
}
}
}
return true;
}
function TIFFendian2Int($bytestring, $byteorder) {
if ($byteorder == 'Intel') {
return getid3_lib::LittleEndian2Int($bytestring);
} elseif ($byteorder == 'Motorola') {
return getid3_lib::BigEndian2Int($bytestring);
}
return false;
}
function TIFFcompressionMethod($id) {
static $TIFFcompressionMethod = array();
if (empty($TIFFcompressionMethod)) {
$TIFFcompressionMethod = array(
1 => 'Uncompressed',
2 => 'Huffman',
3 => 'Fax - CCITT 3',
5 => 'LZW',
32773 => 'PackBits',
);
}
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
}
function TIFFcommentName($id) {
static $TIFFcommentName = array();
if (empty($TIFFcommentName)) {
$TIFFcommentName = array(
270 => 'imagedescription',
271 => 'make',
272 => 'model',
305 => 'software',
306 => 'datetime',
315 => 'artist',
316 => 'hostcomputer',
);
}
return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
}
}
?>
+32
View File
@@ -0,0 +1,32 @@
<?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.doc.php //
// module for analyzing MS Office (.doc, .xls, etc) files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_doc
{
function getid3_doc(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'doc';
$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
return false;
}
}
?>
+59
View File
@@ -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.misc.exe.php //
// module for analyzing EXE files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_exe
{
function getid3_exe(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$EXEheader = fread($fd, 28);
if (substr($EXEheader, 0, 2) != 'MZ') {
$ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.';
return false;
}
$ThisFileInfo['fileformat'] = 'exe';
$ThisFileInfo['exe']['mz']['magic'] = 'MZ';
$ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
$ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
$ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
$ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
$ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
$ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
$ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
$ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
$ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
$ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
$ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
$ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
$ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size'];
$ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16;
$ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
$ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()';
return false;
}
}
?>
+386
View File
@@ -0,0 +1,386 @@
<?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.iso.php //
// module for analyzing ISO files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_iso
{
function getid3_iso($fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'iso';
for ($i = 16; $i <= 19; $i++) {
fseek($fd, 2048 * $i, SEEK_SET);
$ISOheader = fread($fd, 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);
break;
case 2:
$ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
$this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo);
break;
default:
// skip
break;
}
}
}
$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);
}
return true;
}
function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
// 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'];
$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']);
return false;
}
$thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
//$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
$thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
$thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
//$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
$thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
//$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
$thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
$thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
$thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
$thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
$thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
$thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
$thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
$thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
$thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
$thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
$thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
$thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
$thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
$thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
$thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
$thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
$thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
$thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
$thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
$thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
$thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
//$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
$thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
//$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
$thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']);
$thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
$thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
$thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
$thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
$thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']);
$thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
$thisfile_iso_primaryVD['abstract_file_identifier'] = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']);
$thisfile_iso_primaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_primaryVD_raw['bibliographic_file_identifier']);
$thisfile_iso_primaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']);
$thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']);
$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?)';
}
return true;
}
function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
// 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'];
$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']);
return false;
}
$thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
//$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
$thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
$thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
//$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
$thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
// Supplementary Volume Descriptor not used
//unset($thisfile_iso_supplementaryVD);
//return false;
}
//$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
$thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
$thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
$thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
$thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
$thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
$thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
$thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
$thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
$thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
$thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
$thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
$thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
$thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
$thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
$thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
$thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
$thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
$thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
$thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
$thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
$thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
//$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
$thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
//$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
$thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
$thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
$thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
$thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
$thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
$thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
$thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
$thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
$thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
$thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
$thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
$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?)';
}
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'])) {
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'];
$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'];
$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'].')';
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);
$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];
$thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['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));
$offset += 1;
$thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['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));
$offset += 2;
$thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['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['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'].'/';
}
$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
$pathcounter++;
}
return true;
}
function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) {
if (isset($ThisFileInfo['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);
while (ord($DirectoryRecordData{0}) > 33) {
$DirectoryRecordData .= fread($fd, 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));
$ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
$ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
$ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
$ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
$ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
$ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
$ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
$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['filesize'] = $ThisDirectoryRecord['raw']['filesize'];
$ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048;
$ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01);
$ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02);
$ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x04);
$ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x08);
$ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10);
$ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80);
$ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']);
if ($ThisDirectoryRecord['file_flags']['directory']) {
$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']));
}
$DirectoryRecord[] = $ThisDirectoryRecord;
$DirectoryRecordData = fread($fd, 1);
}
return $DirectoryRecord;
}
function ISOstripFilenameVersion($ISOfilename) {
// convert 'filename.ext;1' to 'filename.ext'
if (!strstr($ISOfilename, ';')) {
return $ISOfilename;
} else {
return substr($ISOfilename, 0, strpos($ISOfilename, ';'));
}
}
function ISOtimeText2UNIXtime($ISOtime) {
$UNIXyear = (int) substr($ISOtime, 0, 4);
$UNIXmonth = (int) substr($ISOtime, 4, 2);
$UNIXday = (int) substr($ISOtime, 6, 2);
$UNIXhour = (int) substr($ISOtime, 8, 2);
$UNIXminute = (int) substr($ISOtime, 10, 2);
$UNIXsecond = (int) substr($ISOtime, 12, 2);
if (!$UNIXyear) {
return false;
}
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
}
function ISOtime2UNIXtime($ISOtime) {
// Represented by seven bytes:
// 1: Number of years since 1900
// 2: Month of the year from 1 to 12
// 3: Day of the Month from 1 to 31
// 4: Hour of the day from 0 to 23
// 5: Minute of the hour from 0 to 59
// 6: second of the minute from 0 to 59
// 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East)
$UNIXyear = ord($ISOtime{0}) + 1900;
$UNIXmonth = ord($ISOtime{1});
$UNIXday = ord($ISOtime{2});
$UNIXhour = ord($ISOtime{3});
$UNIXminute = ord($ISOtime{4});
$UNIXsecond = ord($ISOtime{5});
$GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
}
function TwosCompliment2Decimal($BinaryValue) {
// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
// First check if the number is negative or positive by looking at the sign bit.
// If it is positive, simply convert it to decimal.
// If it is negative, make it positive by inverting the bits and adding one.
// Then, convert the result to decimal.
// The negative of this number is the value of the original binary.
if ($BinaryValue & 0x80) {
// negative number
return (0 - ((~$BinaryValue & 0xFF) + 1));
} else {
// positive number
return $BinaryValue;
}
}
}
?>
+32
View File
@@ -0,0 +1,32 @@
<?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.doc.php //
// module for analyzing MS Office (.doc, .xls, etc) files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_doc
{
function getid3_doc(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'doc';
$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
return false;
}
}
?>
+32
View File
@@ -0,0 +1,32 @@
<?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.par2.php //
// module for analyzing PAR2 files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_par2
{
function getid3_par2(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'par2';
$ThisFileInfo['error'][] = 'PAR2 parsing not enabled in this version of getID3()';
return false;
}
}
?>
+32
View File
@@ -0,0 +1,32 @@
<?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.pdf.php //
// module for analyzing PDF files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_pdf
{
function getid3_pdf(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'pdf';
$ThisFileInfo['error'][] = 'PDF parsing not enabled in this version of getID3()';
return false;
}
}
?>
+290
View File
@@ -0,0 +1,290 @@
<?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.apetag.php //
// module for analyzing APE tags //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_apetag
{
function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than 2GB';
return false;
}
$id3v1tagsize = 128;
$apetagheadersize = 32;
$lyrics3tagsize = 10;
if ($overrideendoffset == 0) {
fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
$APEfooterID3v1 = fread($fd, $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;
//} 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'];
}
} else {
fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
if (fread($fd, 8) == 'APETAGEX') {
$ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
}
}
if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
// APE tag not found
unset($ThisFileInfo['ape']);
return false;
}
// shortcut
$thisfile_ape = &$ThisFileInfo['ape'];
fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
$APEfooterData = fread($fd, 32);
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
$ThisFileInfo['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);
} 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']);
}
$ThisFileInfo['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 ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
unset($ThisFileInfo['warning'][$key]);
sort($ThisFileInfo['warning']);
break;
}
}
}
$offset = 0;
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
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'];
return false;
}
}
// shortcut
$ThisFileInfo['replay_gain'] = array();
$thisfile_replaygain = &$ThisFileInfo['replay_gain'];
for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
$offset += 4;
$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);
return false;
}
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
$item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
// shortcut
$thisfile_ape['items'][$item_key] = array();
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
$offset += $value_size;
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
case 0: // UTF-8
case 3: // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
break;
default: // binary data
break;
}
switch (strtolower($item_key)) {
case 'replaygain_track_gain':
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
break;
case 'replaygain_track_peak':
$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].'")';
}
break;
case 'replaygain_album_gain':
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
break;
case 'replaygain_album_peak':
$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].'")';
}
break;
case 'mp3gain_undo':
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
break;
case 'mp3gain_minmax':
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
break;
case 'mp3gain_album_minmax':
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
break;
case 'tracknumber':
foreach ($thisfile_ape_items_current['data'] as $comment) {
$thisfile_ape['comments']['track'][] = $comment;
}
break;
default:
foreach ($thisfile_ape_items_current['data'] as $comment) {
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
}
break;
}
}
if (empty($thisfile_replaygain)) {
unset($ThisFileInfo['replay_gain']);
}
return true;
}
function parseAPEheaderFooter($APEheaderFooterData) {
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
// shortcut
$headerfooterinfo['raw'] = array();
$headerfooterinfo_raw = &$headerfooterinfo['raw'];
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
return false;
}
$headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
$headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
$headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
$headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
$headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
if ($headerfooterinfo['tag_version'] >= 2) {
$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
}
return $headerfooterinfo;
}
function parseAPEtagFlags($rawflagint) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading."
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
$flags['header'] = (bool) ($rawflagint & 0x80000000);
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
$flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
$flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
return $flags;
}
function APEcontentTypeFlagLookup($contenttypeid) {
static $APEcontentTypeFlagLookup = array(
0 => 'utf-8',
1 => 'binary',
2 => 'external',
3 => 'reserved'
);
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
}
function APEtagItemIsUTF8Lookup($itemkey) {
static $APEtagItemIsUTF8Lookup = array(
'title',
'subtitle',
'artist',
'album',
'debut album',
'publisher',
'conductor',
'track',
'composer',
'comment',
'copyright',
'publicationright',
'file',
'year',
'record date',
'record location',
'genre',
'media',
'related',
'isrc',
'abstract',
'language',
'bibliography'
);
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
}
}
?>
+361
View File
@@ -0,0 +1,361 @@
<?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.id3v1.php //
// module for analyzing ID3v1 tags //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_id3v1
{
function getid3_id3v1(&$fd, &$ThisFileInfo) {
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
return false;
}
fseek($fd, -256, SEEK_END);
$preid3v1 = fread($fd, 128);
$id3v1tag = fread($fd, 128);
if (substr($id3v1tag, 0, 3) == 'TAG') {
$ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
// If second-last byte of comment field is null and last byte of comment field is non-null
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
}
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
if (!empty($ParsedID3v1['genre'])) {
unset($ParsedID3v1['genreid']);
}
if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
unset($ParsedID3v1['genre']);
}
foreach ($ParsedID3v1 as $key => $value) {
$ParsedID3v1['comments'][$key][0] = $value;
}
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
$ParsedID3v1['title'],
$ParsedID3v1['artist'],
$ParsedID3v1['album'],
$ParsedID3v1['year'],
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
$ParsedID3v1['comment'],
@$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';
}
$ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize'];
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
$ThisFileInfo['id3v1'] = $ParsedID3v1;
}
if (substr($preid3v1, 0, 3) == 'TAG') {
// The way iTunes handles tags is, well, brain-damaged.
// It completely ignores v1 if ID3v2 is present.
// This goes as far as adding a new v1 tag *even if there already is one*
// A suspected double-ID3v1 tag has been detected, but it could be that
// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
if (substr($preid3v1, 96, 8) == 'APETAGEX') {
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch
} elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
// 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;
}
}
return true;
}
function cutfield($str) {
return trim(substr($str, 0, strcspn($str, "\x00")));
}
function ArrayOfGenres($allowSCMPXextended=false) {
static $GenreLookup = array(
0 => 'Blues',
1 => 'Classic Rock',
2 => 'Country',
3 => 'Dance',
4 => 'Disco',
5 => 'Funk',
6 => 'Grunge',
7 => 'Hip-Hop',
8 => 'Jazz',
9 => 'Metal',
10 => 'New Age',
11 => 'Oldies',
12 => 'Other',
13 => 'Pop',
14 => 'R&B',
15 => 'Rap',
16 => 'Reggae',
17 => 'Rock',
18 => 'Techno',
19 => 'Industrial',
20 => 'Alternative',
21 => 'Ska',
22 => 'Death Metal',
23 => 'Pranks',
24 => 'Soundtrack',
25 => 'Euro-Techno',
26 => 'Ambient',
27 => 'Trip-Hop',
28 => 'Vocal',
29 => 'Jazz+Funk',
30 => 'Fusion',
31 => 'Trance',
32 => 'Classical',
33 => 'Instrumental',
34 => 'Acid',
35 => 'House',
36 => 'Game',
37 => 'Sound Clip',
38 => 'Gospel',
39 => 'Noise',
40 => 'Alt. Rock',
41 => 'Bass',
42 => 'Soul',
43 => 'Punk',
44 => 'Space',
45 => 'Meditative',
46 => 'Instrumental Pop',
47 => 'Instrumental Rock',
48 => 'Ethnic',
49 => 'Gothic',
50 => 'Darkwave',
51 => 'Techno-Industrial',
52 => 'Electronic',
53 => 'Pop-Folk',
54 => 'Eurodance',
55 => 'Dream',
56 => 'Southern Rock',
57 => 'Comedy',
58 => 'Cult',
59 => 'Gangsta Rap',
60 => 'Top 40',
61 => 'Christian Rap',
62 => 'Pop/Funk',
63 => 'Jungle',
64 => 'Native American',
65 => 'Cabaret',
66 => 'New Wave',
67 => 'Psychedelic',
68 => 'Rave',
69 => 'Showtunes',
70 => 'Trailer',
71 => 'Lo-Fi',
72 => 'Tribal',
73 => 'Acid Punk',
74 => 'Acid Jazz',
75 => 'Polka',
76 => 'Retro',
77 => 'Musical',
78 => 'Rock & Roll',
79 => 'Hard Rock',
80 => 'Folk',
81 => 'Folk/Rock',
82 => 'National Folk',
83 => 'Swing',
84 => 'Fast-Fusion',
85 => 'Bebob',
86 => 'Latin',
87 => 'Revival',
88 => 'Celtic',
89 => 'Bluegrass',
90 => 'Avantgarde',
91 => 'Gothic Rock',
92 => 'Progressive Rock',
93 => 'Psychedelic Rock',
94 => 'Symphonic Rock',
95 => 'Slow Rock',
96 => 'Big Band',
97 => 'Chorus',
98 => 'Easy Listening',
99 => 'Acoustic',
100 => 'Humour',
101 => 'Speech',
102 => 'Chanson',
103 => 'Opera',
104 => 'Chamber Music',
105 => 'Sonata',
106 => 'Symphony',
107 => 'Booty Bass',
108 => 'Primus',
109 => 'Porn Groove',
110 => 'Satire',
111 => 'Slow Jam',
112 => 'Club',
113 => 'Tango',
114 => 'Samba',
115 => 'Folklore',
116 => 'Ballad',
117 => 'Power Ballad',
118 => 'Rhythmic Soul',
119 => 'Freestyle',
120 => 'Duet',
121 => 'Punk Rock',
122 => 'Drum Solo',
123 => 'A Cappella',
124 => 'Euro-House',
125 => 'Dance Hall',
126 => 'Goa',
127 => 'Drum & Bass',
128 => 'Club-House',
129 => 'Hardcore',
130 => 'Terror',
131 => 'Indie',
132 => 'BritPop',
133 => 'Negerpunk',
134 => 'Polsk Punk',
135 => 'Beat',
136 => 'Christian Gangsta Rap',
137 => 'Heavy Metal',
138 => 'Black Metal',
139 => 'Crossover',
140 => 'Contemporary Christian',
141 => 'Christian Rock',
142 => 'Merengue',
143 => 'Salsa',
144 => 'Trash Metal',
145 => 'Anime',
146 => 'JPop',
147 => 'Synthpop',
255 => 'Unknown',
'CR' => 'Cover',
'RX' => 'Remix'
);
static $GenreLookupSCMPX = array();
if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
$GenreLookupSCMPX = $GenreLookup;
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
// Extended ID3v1 genres invented by SCMPX
// Note that 255 "Japanese Anime" conflicts with standard "Unknown"
$GenreLookupSCMPX[240] = 'Sacred';
$GenreLookupSCMPX[241] = 'Northern Europe';
$GenreLookupSCMPX[242] = 'Irish & Scottish';
$GenreLookupSCMPX[243] = 'Scotland';
$GenreLookupSCMPX[244] = 'Ethnic Europe';
$GenreLookupSCMPX[245] = 'Enka';
$GenreLookupSCMPX[246] = 'Children\'s Song';
$GenreLookupSCMPX[247] = 'Japanese Sky';
$GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
$GenreLookupSCMPX[249] = 'Japanese Doom Rock';
$GenreLookupSCMPX[250] = 'Japanese J-POP';
$GenreLookupSCMPX[251] = 'Japanese Seiyu';
$GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
$GenreLookupSCMPX[253] = 'Japanese Moemoe';
$GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
//$GenreLookupSCMPX[255] = 'Japanese Anime';
}
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
}
function LookupGenreName($genreid, $allowSCMPXextended=true) {
switch ($genreid) {
case 'RX':
case 'CR':
break;
default:
$genreid = intval($genreid); // to handle 3 or '3' or '03'
break;
}
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
}
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;
}
}
return false;
}
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
}
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='') {
$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);
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
if (!empty($track) && ($track > 0) && ($track <= 255)) {
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
$ID3v1Tag .= "\x00";
if (gettype($track) == 'string') {
$track = (int) $track;
}
$ID3v1Tag .= chr($track);
} else {
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
}
if (($genreid < 0) || ($genreid > 147)) {
$genreid = 255; // 'unknown' genre
}
switch (gettype($genreid)) {
case 'string':
case 'integer':
$ID3v1Tag .= chr(intval($genreid));
break;
default:
$ID3v1Tag .= chr(255); // 'unknown' genre
break;
}
return $ID3v1Tag;
}
}
?>
File diff suppressed because it is too large Load Diff
+282
View File
@@ -0,0 +1,282 @@
<?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.lyrics3.php //
// module for analyzing Lyrics3 tags //
// dependencies: module.tag.apetag.php (optional) //
// ///
/////////////////////////////////////////////////////////////////
class getid3_lyrics3
{
function getid3_lyrics3(&$fd, &$ThisFileInfo) {
// 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';
return false;
}
fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size]
$lyrics3_id3v1 = fread($fd, 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
if ($lyrics3end == 'LYRICSEND') {
// Lyrics3v1, ID3v1, no APE
$lyrics3size = 5100;
$lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
$lyrics3version = 1;
} elseif ($lyrics3end == 'LYRICS200') {
// Lyrics3v2, ID3v1, no APE
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
$lyrics3offset = $ThisFileInfo['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;
$lyrics3version = 1;
$lyrics3offset = $ThisFileInfo['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;
$lyrics3version = 2;
} else {
if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) {
fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET);
$lyrics3lsz = fread($fd, 6);
$lyrics3end = fread($fd, 9);
if ($lyrics3end == 'LYRICSEND') {
// Lyrics3v1, APE, maybe ID3v1
$lyrics3size = 5100;
$lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
$ThisFileInfo['avdataend'] = $lyrics3offset;
$lyrics3version = 1;
$ThisFileInfo['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;
$lyrics3version = 2;
$ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
}
}
}
if (isset($lyrics3offset)) {
$ThisFileInfo['avdataend'] = $lyrics3offset;
$this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size);
if (!isset($ThisFileInfo['ape'])) {
$GETID3_ERRORARRAY = &$ThisFileInfo['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);
}
}
}
return true;
}
function getLyrics3Data(&$ThisFileInfo, &$fd, $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';
return false;
}
fseek($fd, $endoffset, SEEK_SET);
if ($length <= 0) {
return false;
}
$rawdata = fread($fd, $length);
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'];
$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
$length = strlen($rawdata);
} else {
$ThisFileInfo['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:
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
$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';
return false;
}
break;
case 2:
if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
$ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
$rawdata = $ParsedLyrics3['raw']['unparsed'];
while (strlen($rawdata) > 0) {
$fieldname = substr($rawdata, 0, 3);
$fieldsize = (int) substr($rawdata, 3, 5);
$ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
$rawdata = substr($rawdata, 3 + 5 + $fieldsize);
}
if (isset($ParsedLyrics3['raw']['IND'])) {
$i = 0;
$flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
foreach ($flagnames as $flagname) {
if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
$ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
}
}
}
$fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
foreach ($fieldnametranslation as $key => $value) {
if (isset($ParsedLyrics3['raw'][$key])) {
$ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
}
}
if (isset($ParsedLyrics3['raw']['IMG'])) {
$imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
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]);
}
}
}
if (isset($ParsedLyrics3['raw']['LYR'])) {
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
}
} else {
$ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 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)';
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 ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
unset($ThisFileInfo['warning'][$key]);
sort($ThisFileInfo['warning']);
break;
}
}
}
$ThisFileInfo['lyrics3'] = $ParsedLyrics3;
return true;
}
function Lyrics3Timestamp2Seconds($rawtimestamp) {
if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) {
return (int) (($regs[1] * 60) + $regs[2]);
}
return false;
}
function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
foreach ($lyricsarray as $key => $lyricline) {
$regs = array();
unset($thislinetimestamps);
while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) {
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
$lyricline = str_replace($regs[0], '', $lyricline);
}
$notimestamplyricsarray[$key] = $lyricline;
if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
sort($thislinetimestamps);
foreach ($thislinetimestamps as $timestampkey => $timestamp) {
if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
// timestamps only have a 1-second resolution, it's possible that multiple lines
// could have the same timestamp, if so, append
$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
} else {
$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
}
}
}
}
$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
ksort($Lyrics3data['synchedlyrics']);
}
return true;
}
function IntString2Bool($char) {
if ($char == '1') {
return true;
} elseif ($char == '0') {
return false;
}
return null;
}
}
?>
+228
View File
@@ -0,0 +1,228 @@
<?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.apetag.php //
// module for writing APE tags //
// dependencies: module.tag.apetag.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
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
function getid3_write_apetag() {
return true;
}
function WriteAPEtag() {
// NOTE: All data passed to this function must be UTF-8 format
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($this->filename);
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
// Current APE tag between Lyrics3 and ID3v1/EOF
// This break Lyrics3 functionality
if (!$this->DeleteAPEtag()) {
return false;
}
$ThisFileInfo = $getID3->analyze($this->filename);
}
}
if ($this->always_preserve_replaygain) {
$ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
foreach ($ReplayGainTagsToPreserve as $rg_key) {
if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
$this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
}
}
}
if ($APEtag = $this->GenerateAPEtag()) {
if ($fp = @fopen($this->filename, 'a+b')) {
$oldignoreuserabort = ignore_user_abort(true);
flock($fp, LOCK_EX);
$PostAPEdataOffset = $ThisFileInfo['avdataend'];
if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
}
if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
}
fseek($fp, $PostAPEdataOffset, SEEK_SET);
$PostAPEdata = '';
if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
}
fseek($fp, $PostAPEdataOffset, SEEK_SET);
if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
}
ftruncate($fp, ftell($fp));
fwrite($fp, $APEtag, strlen($APEtag));
if (!empty($PostAPEdata)) {
fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
}
flock($fp, LOCK_UN);
fclose($fp);
ignore_user_abort($oldignoreuserabort);
return true;
}
return false;
}
return false;
}
function DeleteAPEtag() {
$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')) {
flock($fp, LOCK_EX);
$oldignoreuserabort = ignore_user_abort(true);
fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
$DataAfterAPE = '';
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
}
ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
if (!empty($DataAfterAPE)) {
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
}
flock($fp, LOCK_UN);
fclose($fp);
ignore_user_abort($oldignoreuserabort);
return true;
}
return false;
}
return true;
}
function GenerateAPEtag() {
// NOTE: All data passed to this function must be UTF-8 format
$items = array();
if (!is_array($this->tag_data)) {
return false;
}
foreach ($this->tag_data as $key => $arrayofvalues) {
if (!is_array($arrayofvalues)) {
return false;
}
$valuestring = '';
foreach ($arrayofvalues as $value) {
$valuestring .= str_replace("\x00", '', $value)."\x00";
}
$valuestring = rtrim($valuestring, "\x00");
// Length of the assigned value in bytes
$tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
$tagitem .= "\x00\x00\x00\x00";
$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
$tagitem .= $valuestring;
$items[] = $tagitem;
}
return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
}
function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
$tagdatalength = 0;
foreach ($items as $itemdata) {
$tagdatalength += strlen($itemdata);
}
$APEheader = 'APETAGEX';
$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
$APEheader .= str_repeat("\x00", 8);
return $APEheader;
}
function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
$APEtagFlags = array_fill(0, 4, 0);
if ($header) {
$APEtagFlags[0] |= 0x80; // Tag contains a header
}
if (!$footer) {
$APEtagFlags[0] |= 0x40; // Tag contains no footer
}
if ($isheader) {
$APEtagFlags[0] |= 0x20; // This is the header, not the footer
}
// 0: Item contains text information coded in UTF-8
// 1: Item contains binary information °)
// 2: Item is a locator of external stored information °°)
// 3: reserved
$APEtagFlags[3] |= ($encodingid << 1);
if ($readonly) {
$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
}
return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
}
function CleanAPEtagItemKey($itemkey) {
$itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
switch (strtoupper($itemkey)) {
case 'EAN/UPC':
case 'ISBN':
case 'LC':
case 'ISRC':
$itemkey = strtoupper($itemkey);
break;
default:
$itemkey = ucwords($itemkey);
break;
}
return $itemkey;
}
}
?>
+119
View File
@@ -0,0 +1,119 @@
<?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.id3v1.php //
// module for writing ID3v1 tags //
// dependencies: module.tag.id3v1.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
class getid3_write_id3v1
{
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_id3v1() {
return true;
}
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')) {
fseek($fp_source, -128, SEEK_END);
if (fread($fp_source, 3) == 'TAG') {
fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
} else {
fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
}
$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']);
fwrite($fp_source, $new_id3v1_tag_data, 128);
fclose($fp_source);
return true;
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
return false;
}
}
$this->errors[] = 'File is not writeable: '.$this->filename;
return false;
}
function FixID3v1Padding() {
// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
// This function rewrites the ID3v1 tag with correct padding
// Initialize getID3 engine
$getID3 = new getID3;
$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);
}
$this->tag_data = $id3v1data;
return $this->WriteID3v1();
}
return false;
}
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')) {
fseek($fp_source, -128, SEEK_END);
if (fread($fp_source, 3) == 'TAG') {
ftruncate($fp_source, filesize($this->filename) - 128);
} else {
// no ID3v1 tag to begin with - do nothing
}
fclose($fp_source);
return true;
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
}
} else {
$this->errors[] = $this->filename.' is not writeable';
}
return false;
}
}
?>
File diff suppressed because it is too large Load Diff
+78
View File
@@ -0,0 +1,78 @@
<?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.lyrics3.php //
// module for writing Lyrics3 tags //
// dependencies: module.tag.lyrics3.php //
// ///
/////////////////////////////////////////////////////////////////
class getid3_write_lyrics3
{
var $filename;
var $tag_data;
//var $lyrics3_version = 2; // 1 or 2
var $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here
function getid3_write_lyrics3() {
return true;
}
function WriteLyrics3() {
$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')) {
flock($fp, LOCK_EX);
$oldignoreuserabort = ignore_user_abort(true);
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET);
$DataAfterLyrics3 = '';
if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
}
ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
if (!empty($DataAfterLyrics3)) {
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET);
fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
}
flock($fp, LOCK_UN);
fclose($fp);
ignore_user_abort($oldignoreuserabort);
return true;
} else {
$this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
return false;
}
}
// no Lyrics3 present
return true;
}
}
?>
+167
View File
@@ -0,0 +1,167 @@
<?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 (!ini_get('safe_mode')) {
// Create file with new comments
$tempcommentsfilename = tempnam('*', 'getID3');
if ($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 "'.$tempcommentsfilename.'", tags not written';
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="'.$tempcommentsfilename.'" "'.$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='.$tempcommentsfilename.' "'.$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;
}
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
return false;
}
function DeleteMetaFLAC() {
if (!ini_get('safe_mode')) {
$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;
}
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
return false;
}
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: ereg_replace() replaces nulls with empty string (not space)
return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
}
}
?>
+592
View File
@@ -0,0 +1,592 @@
<?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.php //
// module for writing tags (APEv2, ID3v1, ID3v2) //
// dependencies: getid3.lib.php //
// write.apetag.php (optional) //
// write.id3v1.php (optional) //
// write.id3v2.php (optional) //
// write.vorbiscomment.php (optional) //
// write.metaflac.php (optional) //
// write.lyrics3.php (optional) //
// ///
/////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) {
die('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.');
}
// NOTES:
//
// You should pass data here with standard field names as follows:
// * TITLE
// * ARTIST
// * ALBUM
// * TRACKNUMBER
// * COMMENT
// * GENRE
// * YEAR
// * ATTACHED_PICTURE (ID3v2 only)
//
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead
// Pass data here as "TRACKNUMBER" for compatability with all formats
class getid3_writetags
{
// public
var $filename; // absolute filename of file to write tags to
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 $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)
var $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
var $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here
// private
var $ThisFileInfo; // analysis of file before writing
function getid3_writetags() {
return true;
}
function WriteTags() {
if (empty($this->filename)) {
$this->errors[] = 'filename is undefined in getid3_writetags';
return false;
} elseif (!file_exists($this->filename)) {
$this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags';
return false;
}
if (!is_array($this->tagformats)) {
$this->errors[] = 'tagformats must be an array in getid3_writetags';
return false;
}
$TagFormatsToRemove = array();
if (filesize($this->filename) == 0) {
// empty file special case - allow any tag format, don't check existing format
// could be useful if you want to generate tag data for a non-existant file
$this->ThisFileInfo = array('fileformat'=>'');
$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
} else {
$getID3 = new getID3;
$getID3->encoding = $this->tag_encoding;
$this->ThisFileInfo = $getID3->analyze($this->filename);
// check for what file types are allowed on this fileformat
switch (@$this->ThisFileInfo['fileformat']) {
case 'mp3':
case 'mp2':
case 'mp1':
case 'riff': // maybe not officially, but people do it anyway
$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
break;
case 'mpc':
$AllowedTagFormats = array('ape');
break;
case 'flac':
$AllowedTagFormats = array('metaflac');
break;
case 'real':
$AllowedTagFormats = array('real');
break;
case 'ogg':
switch (@$this->ThisFileInfo['audio']['dataformat']) {
case 'flac':
//$AllowedTagFormats = array('metaflac');
$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
return false;
break;
case 'vorbis':
$AllowedTagFormats = array('vorbiscomment');
break;
default:
$this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
return false;
break;
}
break;
default:
$AllowedTagFormats = array();
break;
}
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 .= '" files';
$this->errors[] = $errormessage;
return false;
}
}
// List of other tag formats, removed if requested
if ($this->remove_other_tags) {
foreach ($AllowedTagFormats as $AllowedTagFormat) {
switch ($AllowedTagFormat) {
case 'id3v2.2':
case 'id3v2.3':
case 'id3v2.4':
if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) {
$TagFormatsToRemove[] = 'id3v2';
}
break;
default:
if (!in_array($AllowedTagFormat, $this->tagformats)) {
$TagFormatsToRemove[] = $AllowedTagFormat;
}
break;
}
}
}
}
$WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove);
// Check for required include files and include them
foreach ($WritingFilesToInclude as $tagformat) {
switch ($tagformat) {
case 'ape':
$GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) {
return false;
}
break;
case 'id3v1':
case 'lyrics3':
case 'vorbiscomment':
case 'metaflac':
case 'real':
$GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) {
return false;
}
break;
case 'id3v2.2':
case 'id3v2.3':
case 'id3v2.4':
case 'id3v2':
$GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) {
return false;
}
break;
default:
$this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
return false;
break;
}
}
// Validation of supplied data
if (!is_array($this->tag_data)) {
$this->errors[] = '$tag_data is not an array in WriteTags()';
return false;
}
// convert supplied data array keys to upper case, if they're not already
foreach ($this->tag_data as $tag_key => $tag_array) {
if (strtoupper($tag_key) !== $tag_key) {
$this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key];
unset($this->tag_data[$tag_key]);
}
}
// convert source data array keys to upper case, if they're not already
if (!empty($this->ThisFileInfo['tags'])) {
foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) {
foreach ($tag_data_array as $tag_key => $tag_array) {
if (strtoupper($tag_key) !== $tag_key) {
$this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key];
unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]);
}
}
}
}
// Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
$this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
unset($this->tag_data['TRACK']);
}
// Remove all other tag formats, if requested
if ($this->remove_other_tags) {
$this->DeleteTags($TagFormatsToRemove);
}
// Write data for each tag format
foreach ($this->tagformats as $tagformat) {
$success = false; // overridden if tag writing is successful
switch ($tagformat) {
case 'ape':
$ape_writer = new getid3_write_apetag;
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>';
}
} else {
$this->errors[] = 'FormatDataForAPE() failed';
}
break;
case 'id3v1':
$id3v1_writer = new getid3_write_id3v1;
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>';
}
} else {
$this->errors[] = 'FormatDataForID3v1() failed';
}
break;
case 'id3v2.2':
case 'id3v2.3':
case 'id3v2.4':
$id3v2_writer = new getid3_write_id3v2;
$id3v2_writer->majorversion = intval(substr($tagformat, -1));
$id3v2_writer->paddedlength = $this->id3v2_paddedlength;
if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
$id3v2_writer->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>';
}
} else {
$this->errors[] = 'FormatDataForID3v2() failed';
}
break;
case 'vorbiscomment':
$vorbiscomment_writer = new getid3_write_vorbiscomment;
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>';
}
} else {
$this->errors[] = 'FormatDataForVorbisComment() failed';
}
break;
case 'metaflac':
$metaflac_writer = new getid3_write_metaflac;
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>';
}
} else {
$this->errors[] = 'FormatDataForMetaFLAC() failed';
}
break;
case 'real':
$real_writer = new getid3_write_real;
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>';
}
} else {
$this->errors[] = 'FormatDataForReal() failed';
}
break;
default:
$this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
return false;
break;
}
if (!$success) {
return false;
}
}
return true;
}
function DeleteTags($TagFormatsToDelete) {
foreach ($TagFormatsToDelete as $DeleteTagFormat) {
$success = false; // overridden if tag deletion is successful
switch ($DeleteTagFormat) {
case 'id3v1':
$id3v1_writer = new getid3_write_id3v1;
$id3v1_writer->filename = $this->filename;
if (($success = $id3v1_writer->RemoveID3v1()) === false) {
$this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'id3v2':
$id3v2_writer = new getid3_write_id3v2;
$id3v2_writer->filename = $this->filename;
if (($success = $id3v2_writer->RemoveID3v2()) === false) {
$this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'ape':
$ape_writer = new getid3_write_apetag;
$ape_writer->filename = $this->filename;
if (($success = $ape_writer->DeleteAPEtag()) === false) {
$this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'vorbiscomment':
$vorbiscomment_writer = new getid3_write_vorbiscomment;
$vorbiscomment_writer->filename = $this->filename;
if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) {
$this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'metaflac':
$metaflac_writer = new getid3_write_metaflac;
$metaflac_writer->filename = $this->filename;
if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) {
$this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'lyrics3':
$lyrics3_writer = new getid3_write_lyrics3;
$lyrics3_writer->filename = $this->filename;
if (($success = $lyrics3_writer->DeleteLyrics3()) === false) {
$this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>';
}
break;
case 'real':
$real_writer = new getid3_write_real;
$real_writer->filename = $this->filename;
if (($success = $real_writer->RemoveReal()) === false) {
$this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
}
break;
default:
$this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
return false;
break;
}
if (!$success) {
return false;
}
}
return true;
}
function MergeExistingTagData($TagFormat, &$tag_data) {
// Merge supplied data with existing data, if requested
if ($this->overwrite_tags) {
// do nothing - ignore previous data
} else {
if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
return false;
}
$tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
}
return true;
}
function FormatDataForAPE() {
$ape_tag_data = array();
foreach ($this->tag_data as $tag_key => $valuearray) {
switch ($tag_key) {
case 'ATTACHED_PICTURE':
// ATTACHED_PICTURE is ID3v2 only - ignore
$this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag';
break;
default:
foreach ($valuearray as $key => $value) {
if (is_string($value) || is_numeric($value)) {
$ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
} else {
$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag';
unset($ape_tag_data[$tag_key]);
break;
}
}
break;
}
}
$this->MergeExistingTagData('ape', $ape_tag_data);
return $ape_tag_data;
}
function FormatDataForID3v1() {
$tag_data_id3v1['genreid'] = 255;
if (!empty($this->tag_data['GENRE'])) {
foreach ($this->tag_data['GENRE'] as $key => $value) {
if (getid3_id3v1::LookupGenreID($value) !== false) {
$tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value);
break;
}
}
}
$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'])));
if ($tag_data_id3v1['track'] <= 0) {
$tag_data_id3v1['track'] = '';
}
$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
return $tag_data_id3v1;
}
function FormatDataForID3v2($id3v2_majorversion) {
$tag_data_id3v2 = array();
$ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
$ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
$ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3);
foreach ($this->tag_data as $tag_key => $valuearray) {
$ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key);
switch ($ID3v2_framename) {
case 'APIC':
foreach ($valuearray as $key => $apic_data_array) {
if (isset($apic_data_array['data']) &&
isset($apic_data_array['picturetypeid']) &&
isset($apic_data_array['description']) &&
isset($apic_data_array['mime'])) {
$tag_data_id3v2['APIC'][] = $apic_data_array;
} else {
$this->errors[] = 'ID3v2 APIC data is not properly structured';
return false;
}
}
break;
case '':
$this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type';
// some other data type, don't know how to handle it, ignore it
break;
default:
// most other (text) frames can be copied over as-is
foreach ($valuearray as $key => $value) {
if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) {
// source encoding is valid in ID3v2 - use it with no conversion
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding];
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
} 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);
} else {
// convert data from other encoding to UTF-8
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3;
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
}
}
// These values are not needed for all frame types, but if they're not used no matter
$tag_data_id3v2[$ID3v2_framename][$key]['description'] = '';
$tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language;
}
break;
}
}
$this->MergeExistingTagData('id3v2', $tag_data_id3v2);
return $tag_data_id3v2;
}
function FormatDataForVorbisComment() {
$tag_data_vorbiscomment = $this->tag_data;
// check for multi-line comment values - split out to multiple comments if neccesary
// and convert data to UTF-8 strings
foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) {
str_replace("\r", "\n", $value);
if (strstr($value, "\n")) {
unset($tag_data_vorbiscomment[$tag_key][$key]);
$multilineexploded = explode("\n", $value);
foreach ($multilineexploded as $newcomment) {
if (strlen(trim($newcomment)) > 0) {
$tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment);
}
}
} elseif (is_string($value) || is_numeric($value)) {
$tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
} else {
$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag';
unset($tag_data_vorbiscomment[$tag_key]);
break;
}
}
}
$this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment);
return $tag_data_vorbiscomment;
}
function FormatDataForMetaFLAC() {
// FLAC & OggFLAC use VorbisComments same as OggVorbis
// but require metaflac to do the writing rather than vorbiscomment
return $this->FormatDataForVorbisComment();
}
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']));
$this->MergeExistingTagData('real', $tag_data_real);
return $tag_data_real;
}
}
?>
+295
View File
@@ -0,0 +1,295 @@
<?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 $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)) {
if ($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 (@$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 (@$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 (@$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')) {
ob_start();
if ($fp_temp = fopen($tempfilename, 'wb')) {
rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fwrite($fp_temp, $new_CONT_tag_data);
fseek($fp_source, $AfterOffset, SEEK_SET);
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
fwrite($fp_temp, $buffer, strlen($buffer));
}
fclose($fp_temp);
if (copy($tempfilename, $this->filename)) {
unlink($tempfilename);
fclose($fp_source);
return true;
}
unlink($tempfilename);
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
} else {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
ob_end_clean();
}
fclose($fp_source);
return false;
}
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
return false;
}
}
$this->errors[] = 'File is not writeable: '.$this->filename;
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(strlen(@$this->tag_data['title']), 2);
$CONTchunk .= @$this->tag_data['title'];
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
$CONTchunk .= @$this->tag_data['artist'];
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
$CONTchunk .= @$this->tag_data['copyright'];
$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
$CONTchunk .= @$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)) {
if ($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')) {
ob_start();
if ($fp_temp = fopen($tempfilename, 'wb')) {
rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fseek($fp_source, $AfterOffset, SEEK_SET);
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
fwrite($fp_temp, $buffer, strlen($buffer));
}
fclose($fp_temp);
if (copy($tempfilename, $this->filename)) {
unlink($tempfilename);
fclose($fp_source);
return true;
}
unlink($tempfilename);
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
} else {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
ob_end_clean();
}
fclose($fp_source);
return false;
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
return false;
}
}
$this->errors[] = 'File is not writeable: '.$this->filename;
return false;
}
}
?>
+124
View File
@@ -0,0 +1,124 @@
<?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 (!ini_get('safe_mode')) {
// Create file with new comments
$tempcommentsfilename = tempnam('*', 'getID3');
if ($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;
}
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
return false;
}
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: ereg_replace() replaces nulls with empty string (not space)
return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
}
}
?>
+241
View File
@@ -0,0 +1,241 @@
Version 1.5.7 (2013-04-10)
HTML5: Workaround for squish problem in iOS6, when uploading resized images.
HTML5: Fix failing workaround for FormData+Blob bug on Android...
HTML5: Allow multiple uploads on iOS.
Include VB build script that doesn't depend on Ant and is said to be native to Windows.
Version 1.5.6 (2013-02-28)
Fix regression: getPos() wrong on ie6/7.
HTML5: Send file as binary string if it was mangled, even if multipart is set to false.
HTML5: Add workaround Android browsers, that are unable to properly send blobs in FormData.
HTML4/Flash/Silverlight: Destroy fails to cleanup everything when container not defined.
UI, Queue: Use html() instead of text(), otherwise special characters fail to display properly.
UI, Queue: Use on() instead of delegate() or live(). Update examples to use jQuery 1.9.0 by default.
Queue: Blur rename input field when ESC is pressed.
Additional language packs: Slovak.
Version 1.5.5 (2013-01-23)
UI Widget: Fix sortable feature, broken in jQuery UI 1.9.
Queue: Replace live() with delegate(), as live() was removed from jQuery 1.9.
HTML5: window.getComputedStyle in Firefox doesn't support dashed rulenames - use zIndex instead of z-index.
HTML5/Flash/Silverlight/Gears: Process JPEGs, if quality parameter is present, whatever the scale factor.
Flash: Survive invalid EXIF tag offsets.
Flash: Allow only letters, digits and underscore in runtime id to avoid script injection.
SilverLight: Prepend ampersand to the query string, for non multipart cases (as in Flash and HTML5).
Add mime types for m2v,3gp,3g2 extensions.
Version 1.5.4 (2012-04-12)
Flash: Disable scripting if swf was loaded from another domain.
Version 1.5.3 (2012-04-05)
HTML5: Check if xhr object is initialized, before calling abort() on it.
HTML4: Postpone form removal until uploaders state changes, to avoid error on resuming stopped uploads.
BrowserPlus: Fix mechanical typo, that caused error during mime type check.
BrowserPlus: browserPlus.Uploader.Cancel() has two required parameters, dies with the error if not passed.
Flash: Improve runtime's behaviour during upload cancellation.
Flash: Survive the case when GPSVersionID contains unexpected value.
Flash: Fix random freeze in Chrome's bundled Flash Player.
Flash: Avoid the silent break when URLStream not yet open, but close is called on it.
Flash: Move Destroy handler out of Flash:Init handler, since it might be called not only after Flash:Init but also before it.
Flash: Avoid warning during build with mxmlc.
Try removeEventListener first in IE and only if it fails - detachEvent.
Fix plupload.getPos to return proper value in IE8+.
Do not initiate plupload.STARTED state, if file queue is empty.
Additional language packs: Estonian, Polish, Korean, French-Canadian, Greek, Persian/Farsi.
Version 1.5.2 (2012-01-06)
UI Widget: Do not show UI if no runtime can be initialized.
UI Widget: Timely update file size and total size if resize in action.
UI Widget: Constrain renaming feature to queued files only.
UI Widget: Disable Add button properly, if requested, rather then just hide.
HTML4/HTML5/BrowserPlus: Avoid adding mime type twice to dialog trigger.
HTML5: fix regression, when unresized images were failing on FF3.6.
HTML5: Constrain Gecko 2,5,6 workaround to multipart mode only.
HTML5/Flash: Take into account weird possibilities of ExifVersion being a string, rather then standard Undefined.
Flash: Simplify event dispatching in BitmapDataUnlimited class, in order to avoid freezing on resizing in FP11.
Add ability to disable file dialog trigger on request (uploader.disableBrowse(true/false)).
Support for immediate abort of upload process, be it chunked upload or regular one.
Abort all activity, before destroying uploader.
Revive temporary file removal logic in upload.php.
Fix potential vulnerability in dump.php and upload.php.
Additional MIME types: application/vnd.openxmlformats-officedocument.*, application/x-javascript, application/json, text/css,css, application/vnd.oasis.opendocument.formula-templat.
Additional language packs: Hungarian, Croatian, Serbian, Romanian.
Version 1.5.1.1 (2011-09-27)
HTML5: Fix mechanical typo, that successfully broke drag and drop, wherever could.
Version 1.5.1 (2011-09-26)
HTML4: Add support for server responses in HTML format.
HTML5: Disable multiple file selection in Safari 5.x for Windows (see #363).
HTML5: Gecko 2/5/6 should upload chunks as binary strings when in chunking mode and client side resize is requested.
Flash: Enforce URLStream mode when custom headers are passed.
Flash: Fix embedding problems in IE9 (and all other IEs).
Flash/Gears/BrowserPlus/SilverLight: Expose multi_selection feature, to be used in required_features (mainly to overcome Safari for Windows problem).
SilverLight: Properly handle custom and null headers.
UploadComplete moved to fire after the last StateChanged event.
Additional language packs: Finnish.
Version 1.5b (2011-09-11)
UI Widget: Fix sortable logic.
UI Widget: Fix bug, when message was displayed simultaneously across all Plupload UI instances on the page.
UI Widget: notify() method is now public - users can throw their own messages into the widget header.
HTML4/HTML5: Revise input[type=file] placement logic to support cursor styling on Geko 2+.
HTML5: Revise Drag'n'Drop detection logic.
HTML5: Make Exif and GPS information available to user, introduce two new events: ExifData and GpsData.
HTML5: Add support for File.prototype.slice() method (mozSlice/webkitSlice) in order to be able to upload files in chunks without preloading.
HTML5: Remove any JPEG headers before restoring them, 'cause user agent (like Chrome), might be inserting it's own.
Flash: Remove a limit on the depth of image header check, since it still fails in some cases and performance gain is negligible.
Flash: Fix regression, when runtime hanged when not images where passed in as images.
SilverLight: Fix bug, when JSON serializer was failing on null.
SilverLight: Remove cast to integer for chunk boundary, which resulted in a wrong size for last chunks on huge files.
SilverLight: Increase read buffer, add disposal of ImageStream and FileStream, optimize for performance.
Updated build.xml to include language packs in release package under js/ folder.
Gears/BrowserPlus: Add support for * file filter.
BeforeUpload now can cancel upload if required.
Additional MIME types: text/csv, image/photoshop, video/x-ms-wmv, video/avi, video/webm support
Additional language packs: Japanese
Cleaned examples.
Version 1.4.3.2 (2011-04-13)
Fixed bug in HTML5 runtime, when was reduced by a factor of 100 after every upload.
Version 1.4.3.1 (2011-04-12)
Fixed build script, mistakenly populating jquery.plupload.queue directory from jquery.ui.plupload sources.
Fixed script urls in all examples, build script now will alter them automatically from dev to release when needed.
Fixed isEmptyObj undefined error in HTML4 runtime.
Fixed bug in UI Widget preventing UploadComplete from triggering.
Version 1.4.3 (2011-04-11)
Added Latvian language pack and updated French.
Fixed bug in Flash runtime when JPEG header was not investigated deep enough to reach SOFn marker in large images.
Fixed bug, when PNGs were cropped to width in Flash runtimes, rather then resized.
Fixed Flash to allow multiple uploading of the same file, with different settings.
Fixed Flash runtime to clean anonymous listeners properly.
Fixed HTML5 runtime to resolve to mimeType in case-insensitive way.
Fixed HTML5/Flash/SilverLight/Gears runtimes for inconsistency in naming of chunks feature, comparing to other runtimes.
Fixed HTML4/HTML5 runtimes for input[type=file] to outsize contaner effectively enough to fill the whole click area.
Fixed all runtimes to preserve position (relative/absolute) rule on containers that already have it.
Fixed SilverLight runtime to support large files (over 2GB).
Restructured the examples, src and build scripts to make it more clear that jQuery is optional.
Added support for *.* filter.
Added support for preserving ICC and IPTC headers when resizing JPEGs.
Added Image.onerror/onabort handlers to HTML5 in order to gracefully bypass faulty images.
Added ability to drop image size (by lowering quality), while preserving original dimension (HTML5/Flash/Gears).
Ported EXIF, ICC, IPTC preservation code to Flash runtime.
Version 1.4.2 (2011-02-20)
Added Brazilian Portuguese, German, Russian and Spanish translations.
Added support for file_data_name option to SilverLight runtime.
Added support for better quality image resizing to Flash runtime.
Added support for properly handling images with dimensions up to 8191x8191 pixels to Flash runtime.
Added 'updatelist' event to UI Widget, which will be triggered every time file list will get redrawn.
Added support for dynamically changing options to UI Widget.
Fixed HTML4 runtime bug, when UploadFile handler was attached twice.
Fixed HTML5 to use FileReader.readAsBinaryString() instead of File.getAsBinary() on newer WebKit browsers (like Chrome 9).
Fixed Flash runtime from sending duplicate Filename param, when using FileReference.upload().
Updated S3 example to illustrate support for a proper progress indication.
Version 1.4.1 (2011-02-01)
Added an example on how to use Plupload with Amazon S3 written in PHP but can easily be ported to other languages.
Fixed bug where hidden input elements wasn't created when the multiple_queues option wasn't used.
Fixed bug where FF4 would produce an exception about missing BlobBuilder.
Version 1.4.0 (2011-01-26)
Added removeEvent and removeAllEvents methods and modified addEvent accordingly, in order to support dynamic unload.
Added unbindAll method.
Added UploadComplete event, which fires when internal iterator reaches the end of the queue.
Added public destroy method to plupload object, new event - Destroy, and corresponding handlers to all runtimes.
Added Czech, Italian, French, Dutch translations.
Added support for translatable error messages.
Added two new options: browse_button_hover and browse_button_active, in order to support browse_button interactivity.
Added support for 'multi_selection: false' to Silverlight runtime.
Added support for video/mp4, video/x-m4v and audio/mp4 MIME Types.
Added artificial sendAsBinary method to XMLHttpRequest.prototype for browsers that have support for BlobBuilder and typed arrays.
Added version tracking variable into plupload object and version comment to the header of every file.
Fixed measurements of browse_button element in order to size and position input[type=file] element to fit it fully.
Fixed Flash runtime behavior for multiple_select=false and other simpleUpload usage cases: basically new FileReference has to be created for every select dialog.
Fixed browser sniffer to match only Safari, for fakeSafariDragDrop (seems like Safari on Mac doesn't require it either).
Fixed so that ExternalInterface escapes strings properly, before passing them to JS.
Fixed eventual reinitialization of flash/silverlight runtimes, especially for cases when object wrapper needed to be programmatically hidden and then shown again.
Fixed so that Plupload will now ignore files with duplicate names when adding to the queue, in one set. Mainly introduced to work around Safari on Windows bug (https://bugs.webkit.org/show_bug.cgi?id=37957).
Fixed bug, when final UploadProgress was firing after FileUploaded for Flash simpleUpload.
Fixed bug where upload would fail if an error was produced inside the FilesAdded event.
Fixed bug in Flash runtime when it used a wrong size when resizing, but not chunking.
Fixed bug in Silverlight runtime that would keep sending 0 byte packages when a picture was chunked before resized.
Disabled blur filter (is going to be replaced with some bilinear resampling in next release).
Completely revised UI Widget, to be more jQuery UI oriented. Optionally depends on UI Button, UI Sortable, UI ProgressBar.
Version 1.3.0 (2010-11-24)
Added new jQuery UI widget that supports jQuery UI themes.
Added new multiple_queues option that enables you to upload multiple times in the queue widgets.
Added support for crossdomain loading of the XAP and SWF files and crossdomain upload.
Added new multiple_queues option that enables you to upload multiple times in the queue widgets.
Added support for crossdomain loading of the XAP and SWF files and crossdomain upload.
Added preinit/init options to to ease up the binding of custom events to queueWidget and the Uploader class.
Added drag/drop support for Safari until they fix the broken drag/drop support on Windows.
Added events example file that show how to bind all events and display event specific data.
Added support for retaining Exif data on images when they where resized using the HTML5 runtime.
Fixed logic issue with the upload.php example file. Chunking wasn't working correctly.
Fixed issue with HTML4 not handling the form encoding correctly on older IE versions. Patch contributed by jinxdone.
Fixed so the HTML4 runtime only submits the defined multipart_params arguments.
Fixes issue where it wasn't possible to dynamically override url or mutlipart_params for the HTML4 runtime.
Fixed so all runtimes pass the name, chunk and chunks parameters as multipart parameters instead of querystring parameters.
Fixed so files are read using the newer FileReader class if it's available if not it tries the older getAsXXX on Gecko.
Fixed bug where IE 9 beta 1 wouldn't render Silverlight properly.
Fixed bug where Flash would do extra empty requests if images below a specific size would be uploaded.
Fixed bug where Google Gears would resize and re-encode images even if the it wasn't changed in scale.
Fixed bug where the HTML5 runtime wouldn't free memory after each request on Gecko.
Version 1.2.4 (2010-09-08)
Added new BeforeUpload event to make it easier to override settings before a file is uploaded.
Added new automatic usage of FileReference in Flash if it's possible. Contributed by Marcel Jackwerth.
Added new chunking support for Chrome 5 and Firefox 3.6 using the HTML 5 runtime.
Added new multipart upload support for WebKit using the HTML 5 runtime and the FormData object.
Added new image scaling method for the Flash runtime contributed by rcoopman.
Added new alert error message if the user selected invalid files.
Added new automatic unique name generation to the example.php script. Contributed by Brandon Kelly.
Changed so the default upload method is multipart and the default chunk size is 0.
Fixed progress issue with the HTML5 runtime running on Gecko.
Fixed so longer extensions can be used such as .tar.gz.
Fixed so the file extension is retained when using the unique_names option.
Version 1.2.3 (2010-05-27)
Added new drag/drop support for HTML5 running on Chrome beta.
Added new multipart state for the features object. It's now possible to detect multipart support.
Added new getFeatures function to all runtime. Basic concept by Javier Martinez Fernandez.
Fixed bug where runtimes where initialized even if they didn't match the required_features setting.
Version 1.2.2.1 (2010-05-04)
Added new headers option, enables you to set custom headers for the upload requests.
Fixed bug where the file extension checking was case sensitive.
Version 1.2.2 (2010-04-26)
Added new file_data_name option that enables you to set the multipart file data param. Patch contributed by Alex Ganov.
Added new FILE_SIZE_ERROR type that will be triggered if the user selected a file that is to large or zero bytes.
Added new FILE_EXTENSION_ERROR type that will be triggered if you add a file with an invalid file extension.
Added new required_features setting, enables you to specify a list of required features that the runtime must have.
Fixed so the plupload.buildUrl function uses the UTF compatible encodeURIComponent method instead of escape.
Fixed so that all file types can be selected if you don't specify a filter setting.
Fixed so more valid HTTP status codes are accepted as valid responses.
Fixed so all runtimes fills the features object with available features.
Fixed some issues with the HTML4 runtime if there wasn't any existing forms on the page.
Fixed some conflict issues with HTML4 runtime and forms with the input names of action or target.
Fixed bug where some Gecko versions would produce exceptions when checking the HTTP status of a XHR.
Version 1.2.1 (2010-03-22)
Fixed bug with incorrect aspect ratio in Flash image scaling.
Fixed bug where chunked uploads could get scrambled in the Flash runtime. Patch contributed by Grady Werner.
Fixed bug where a beta version of Chrome wouldn't handle drag/drop correctly because of missing drag effect.
Fixed so the HTML 4 runtime displays N/A for file sizes and the progress is based on uploaded files instead of bytes.
Fixed so chunking can be disabled properly in Flash but that will affect the progress bar.
Fixed so queue widget displays the drag/drop message if file queue is emptied.
Fixed small files are uploaded as one single chunk and not forced into 4 chunks in the Flash runtime.
Version 1.2 (2010-03-09)
Added new rename file support for jQuery queue widget, click on a file name to rename it if it's enabled.
Added official ChunkUploaded event, it similar to FileUploaded but executed for each chunk.
Added bytes per second support to total queue progress.
Added better error handling to core API using the new Error event.
Added better error handling to jQuery queue widget.
Fixed so chunking uploads is dispatch from JS not from inside Flash/Silverlight.
Version 1.1.1 (2010-02-25)
Added new setup setting to queue widget. Makes it easier to bind custom events to uploader instance.
Fixed so it's possible to disable chunking compleatly. It's now disabled by default.
Fixed bug where multipart mode was enabled all the time in the Flash runtime.
Fixed bug where chunked uploading in Silverlight would fail.
Fixed bug where the delete button was visible while uploading.
Fixed bug where unique_names setting wasn't working when the core API was used.
Fixed bug where the queue widget wouldn't display the currently uploaded file if the unique_names was enabled.
Version 1.1 (2010-02-24)
Added new multipart and multipart_params support.
Added new container option, enables you to specify where flash/silverlight objects would be added.
Added chunking support to BrowserPlus runtime, contributed by Steve Spencer.
Added FileUploaded event that fires when a file is uploaded.
Added more easily understandable buttons to queue widget.
Added html4 runtime, contributed by Ryan Demmer.
Fixed issues with i18n support and added a Swedish and Danish language pack.
Fixed bug where the Flash runtime could do empty requests if the image was scaled down.
Fixed bug where uploading small images in Silverlight would produce an exception.
Fixed so the runtime list can include whitespace or missing runtimes. Patch contributed by Øyvind Sean Kinsey.
Fixed so to large files are ignored and never dispatched to the FilesAdded event.
Version 1.0 (2010-02-03)
First official release of Plupload.
+14
View File
@@ -0,0 +1,14 @@
// .po file like language pack
plupload.addI18n({
'Select files' : 'Vyberte soubory',
'Add files to the upload queue and click the start button.' : 'Přidejte soubory do fronty a pak spusťte nahrávání.',
'Filename' : 'Název souboru',
'Status' : 'Status',
'Size' : 'Velikost',
'Add Files' : 'Přidat soubory',
'Stop current upload' : 'Zastavit nahrávání',
'Start uploading queue' : 'Spustit frontu nahrávání',
'Drag files here.' : 'Sem přetáhněte soubory.',
'Start Upload': 'Spustit nahrávání',
'Uploaded %d/%d files': 'Nahráno %d/%d souborů'
});
+12
View File
@@ -0,0 +1,12 @@
// .po file like language pack
plupload.addI18n({
'Select files' : 'Vælg filer',
'Add files to the upload queue and click the start button.' : 'Tilføj filer til køen, og tryk på start.',
'Filename' : 'Filnavn',
'Status' : 'Status',
'Size' : 'Størrelse',
'Add files' : 'Tilføj filer',
'Stop current upload' : 'Stop upload',
'Start uploading queue' : 'Start upload',
'Drag files here.' : 'Træk filer her.'
});
+24
View File
@@ -0,0 +1,24 @@
// German
plupload.addI18n({
'Select files' : 'Dateien hochladen',
'Add files to the upload queue and click the start button.' : 'Dateien hinzuf&uuml;gen und auf \'Hochladen\' klicken.',
'Filename' : 'Dateiname',
'Status' : 'Status',
'Size' : 'Gr&ouml;&szlig;e',
'Add files' : 'Dateien', // hinzuf&uuml;gen',
'Stop current upload' : 'Aktuelles Hochladen stoppen',
'Start uploading queue' : 'Hochladen starten',
'Uploaded %d/%d files': '%d/%d Dateien sind hochgeladen',
'N/A' : 'Nicht verf&uuml;gbar',
'Drag files here.' : 'Ziehen Sie die Dateien hier hin',
'File extension error.': 'Fehler bei Dateiendung',
'File size error.': 'Fehler bei Dateigr&ouml;ße',
'Init error.': 'Initialisierungsfehler',
'HTTP Error.': 'HTTP-Fehler',
'Security error.': 'Sicherheitsfehler',
'Generic error.': 'Typischer Fehler',
'IO error.': 'Ein/Ausgabe-Fehler',
'Stop Upload': 'Hochladen stoppen',
'Start upload': 'Hochladen',
'%d files queued': '%d Dateien in der Warteschlange'
});
+14
View File
@@ -0,0 +1,14 @@
// Greek
plupload.addI18n({
'Select files' : 'Επιλέξτε Αρχεία',
'Add files to the upload queue and click the start button.' : 'Προσθήκη αρχείων στην ουρά μεταφόρτωσης',
'Filename' : 'Όνομα αρχείου',
'Status' : 'Κατάσταση',
'Size' : 'Μέγεθος',
'Add Files' : 'Προσθέστε αρχεία',
'Stop current upload' : 'Διακοπή τρέχουσας μεταφόρτωσης',
'Start uploading queue' : 'Εκκίνηση μεταφόρτωσης ουράς αρχείων',
'Drag files here.' : 'Σύρετε αρχεία εδώ',
'Start Upload': 'Εκκίνηση μεταφόρτωσης',
'Uploaded %d/%d files': 'Ανέβηκαν %d/%d αρχεία'
});
+25
View File
@@ -0,0 +1,25 @@
// Spanish
plupload.addI18n({
'Select files' : 'Elija archivos:',
'Add files to the upload queue and click the start button.' : 'Agregue archivos a la cola de subida y haga click en el boton de iniciar.',
'Filename' : 'Nombre de archivo',
'Status' : 'Estado',
'Size' : 'Tama&ntilde;o',
'Add files' : 'Agregue archivos',
'Stop current upload' : 'Detener subida actual',
'Start uploading queue' : 'Iniciar subida de cola',
'Uploaded %d/%d files': 'Subidos %d/%d archivos',
'N/A' : 'No disponible',
'Drag files here.' : 'Arrastre archivos aqu&iacute;',
'File extension error.': 'Error de extensi&oacute;n de archivo.',
'File size error.': 'Error de tama&ntilde;o de archivo.',
'Init error.': 'Error de inicializaci&oacute;n.',
'HTTP Error.': 'Error de HTTP.',
'Security error.': 'Error de seguridad.',
'Generic error.': 'Error gen&eacute;rico.',
'IO error.': 'Error de entrada/salida.',
'Stop Upload': 'Detener Subida.',
'Add Files': 'Agregar Archivos',
'Start Upload': 'Comenzar Subida.',
'%d files queued': '%d archivos en cola.'
});
+33
View File
@@ -0,0 +1,33 @@
// Estonian translation, et.js
plupload.addI18n({
'Select files' : 'Vali faile',
'Add files to the upload queue and click the start button.' : 'Lisa failid üleslaadimise järjekorda ja klõpsa alustamise nupule.',
'Filename' : 'Failinimi',
'Status' : 'Olek',
'Size' : 'Suurus',
'Add files' : 'Lisa faile',
'Stop current upload' : 'Praeguse üleslaadimise peatamine',
'Start uploading queue' : 'Järjekorras ootavate failide üleslaadimise alustamine',
'Drag files here.' : 'Lohista failid siia.',
'Start upload' : 'Alusta üleslaadimist',
'Uploaded %d/%d files': 'Üles laaditud %d/%d',
'Stop upload': 'Peata üleslaadimine',
'Start upload': 'Alusta üleslaadimist',
'%d files queued': 'Järjekorras on %d faili',
'File: %s': 'Fail: %s',
'Close': 'Sulge',
'Using runtime: ': 'Kasutatakse varianti: ',
'File: %f, size: %s, max file size: %m': 'Fail: %f, suurus: %s, suurim failisuurus: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Üleslaadimise element saab vastu võtta ainult %d faili ühe korraga. Ülejäänud failid jäetakse laadimata.',
'Upload URL might be wrong or doesn\'t exist': 'Üleslaadimise URL võib olla vale või seda pole',
'Error: File too large: ': 'Viga: fail on liiga suur: ',
'Error: Invalid file extension: ': 'Viga: sobimatu faililaiend: ',
'File extension error.': 'Faililaiendi viga.',
'File size error.': 'Failisuuruse viga.',
'File count error.': 'Failide arvu viga.',
'Init error.': 'Lähtestamise viga.',
'HTTP Error.': 'HTTP ühenduse viga.',
'Security error.': 'Turvaviga.',
'Generic error.': 'Üldine viga.',
'IO error.': 'S/V (I/O) viga.'
});
+37
View File
@@ -0,0 +1,37 @@
// Persian
plupload.addI18n({
'Select files' : 'انتخاب فایل',
'Add files to the upload queue and click the start button.' : 'اضافه کنید فایل ها را به صف آپلود و دکمه شروع را کلیک کنید.',
'Filename' : 'نام فایل',
'Status' : 'وضعیت',
'Size' : 'سایز',
'Add Files' : 'افزودن فایل',
'Stop Upload' : 'توقف انتقال',
'Start Upload' : 'شروع انتقال',
'Add files' : 'افزودن فایل',
'Add files.' : 'افزودن فایل',
'Stop current upload' : 'توقف انتقال جاری',
'Start uploading queue' : 'شروع صف انتقال',
'Stop upload' : 'توقف انتقال',
'Start upload' : 'شروع انتقال',
'Uploaded %d/%d files': 'منتقل شد %d/%d از فایلها',
'N/A' : 'N/A',
'Drag files here.' : 'بکشید فایل ها رو به اینجا',
'File extension error.': 'خطا پیشوند فایل',
'File size error.': 'خطای سایز فایل',
'File count error.': 'خطای تعداد فایل',
'Init error.': 'خطا در استارت اسکریپت',
'HTTP Error.': 'HTTP خطای',
'Security error.': 'خطای امنیتی',
'Generic error.': 'خطای عمومی',
'IO error.': 'IO خطای',
'File: %s': ' فایل ها : %s',
'Close': 'بستن',
'%d files queued': '%d فایل در صف',
'Using runtime: ': 'استفاده میکنید از : ',
'File: %f, size: %s, max file size: %m': فایل: %f, سایز: %s, بزرگترین سایز فایل: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'عنصر بارگذار فقط %d فایل رو در یک زمان می پذیرد. سایر فایل ها مجرد از این موضوع هستند.',
'Upload URL might be wrong or doesn\'t exist': 'آدرس آپلود اشتباه می باشد یا وجود ندارد',
'Error: File too large: ': 'خطا: فایل حجیم است :: ',
'Error: Invalid file extension: ': 'خطا پسوند فایل معتبر نمی باشد : '
});
+33
View File
@@ -0,0 +1,33 @@
// .fi file like language pack
plupload.addI18n({
'Select files' : 'Valitse tiedostoja',
'Add files to the upload queue and click the start button.' : 'Lisää tiedostoja latausjonoon ja klikkaa aloita-nappia.',
'Filename' : 'Tiedostonimi',
'Status' : 'Tila',
'Size' : 'Koko',
'Add files' : 'Lisää tiedostoja',
'Stop current upload' : 'Pysäytä nykyinen lataus',
'Start uploading queue' : 'Aloita jonon lataus',
'Drag files here.' : 'Raahaa tiedostot tänne.',
'Start upload' : 'Aloita lataus',
'Uploaded %d/%d files': 'Ladattu %d/%d tiedostoa',
'Stop upload': 'Pysäytä lataus',
'Start upload': 'Aloita lataus',
'%d files queued': '%d tiedostoa jonossa',
'File: %s': 'Tiedosto: %s',
'Close': 'Sulje',
'Using runtime: ': 'Käytetään ajonaikaista: ',
'File: %f, size: %s, max file size: %m': 'Tiedosto: %f, koko: %s, maksimi tiedostokoko: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Latauselementti sallii ladata vain %d tiedosto(a) kerrallaan. Ylimääräiset tiedostot ohitettiin.',
'Upload URL might be wrong or doesn\'t exist': 'Lataus URL saattaa olla väärin tai ei ole olemassa',
'Error: File too large: ': 'Virhe: Tiedosto liian suuri: ',
'Error: Invalid file extension: ': 'Virhe: Kelpaamaton tiedostopääte: ',
'File extension error.': 'Tiedostopäätevirhe.',
'File size error.': 'Tiedostokokovirhe.',
'File count error.': 'Tiedostolaskentavirhe.',
'Init error.': 'Init virhe.',
'HTTP Error.': 'HTTP virhe.',
'Security error.': 'Tietoturvavirhe.',
'Generic error.': 'Yleinen virhe.',
'IO error.': 'I/O virhe.'
});
+35
View File
@@ -0,0 +1,35 @@
// French-Canadian
plupload.addI18n({
'Select files' : 'Sélectionnez les fichiers',
'Add files to the upload queue and click the start button.' : 'Ajoutez des fichiers à la file d\'attente et appuyez sur le bouton démarrer.',
'Filename' : 'Nom du fichier',
'Status' : 'Statut',
'Size' : 'Taille',
'Add files' : 'Ajouter Fichiers',
'Stop current upload' : 'Arrêter le téléversement actuel',
'Start uploading queue' : 'Démarrer le téléversement',
'Uploaded %d/%d files': '%d/%d fichiers envoyés',
'N/A' : 'Non applicable',
'Drag files here.' : 'Glisser-déposer les fichiers ici',
'File extension error.': 'Erreur d\'extension de fichier',
'File size error.': 'Erreur de taille de fichier',
'Init error.': 'Erreur d\'initialisation',
'HTTP Error.': 'Erreur HTTP',
'Security error.': 'Erreur de sécurité',
'Generic error.': 'Erreur commune',
'IO error.': 'Erreur E/S',
'Stop Upload': 'Arrêter le téléversement',
'Add Files': 'Ajouter des fichiers',
'Start upload': 'Démarrer le téléversement',
'%d files queued': '%d fichiers en attente',
'File: %s':'Fichier: %s',
'Close':'Fermer',
'Using runtime:':'Moteur logiciel:',
'File: %f, size: %s, max file size: %m':'Fichier: %f, poids: %s, poids maximal: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.':'La file accepte %d fichier(s) à la fois. Les fichiers en trop sont ignorés',
'Upload URL might be wrong or doesn\'t exist':'L\'URL de téléversement est erroné ou inexistant',
'Error: File to large: ':'Fichier trop volumineux: ',
'Error: Invalid file extension: ':'Extension de fichier invalide: ',
'File size error.':'Erreur de taile de fichier',
'File count error.':'Erreur de décompte des fichiers'
});
+25
View File
@@ -0,0 +1,25 @@
// French
plupload.addI18n({
'Select files' : 'Sélectionnez les fichiers',
'Add files to the upload queue and click the start button.' : 'Ajoutez des fichiers à la file et appuyez sur le bouton démarrer.',
'Filename' : 'Nom de fichier',
'Status' : 'Status',
'Size' : 'Taille',
'Add files' : 'Ajouter Fichiers',
'Stop current upload' : 'Arrêter l\'envoi en cours',
'Start uploading queue' : 'Démarrer l\'envoi',
'Uploaded %d/%d files': '%d/%d fichiers envoyés',
'N/A' : 'Non applicable',
'Drag files here.' : 'Déposer les fichiers ici.',
'File extension error.': 'Erreur extension fichier',
'File size error.': 'Erreur taille fichier.',
'Init error.': 'Erreur d\'initialisation.',
'HTTP Error.': 'Erreur HTTP.',
'Security error.': 'Erreur de sécurité.',
'Generic error.': 'Erreur générique.',
'IO error.': 'Erreur E/S.',
'Stop Upload': 'Arrêter les envois.',
'Add Files': 'Ajouter des fichiers',
'Start Upload': 'Démarrer les envois.',
'%d files queued': '%d fichiers en attente.'
});
+25
View File
@@ -0,0 +1,25 @@
// Croatian
plupload.addI18n({
'Select files': 'Izaberite datoteke:',
'Add files to the upload queue and click the start button.': 'Dodajte datoteke u listu i kliknite Upload.',
'Filename': 'Ime datoteke',
'Status': 'Status',
'Size': 'Veličina',
'Add files': 'Dodajte datoteke',
'Stop current upload': 'Zaustavi trenutan upload',
'Start uploading queue': 'Pokreni Upload',
'Uploaded %d/%d files': 'Uploadano %d/%d datoteka',
'N/A': 'N/A',
'Drag files here.': 'Dovucite datoteke ovdje',
'File extension error.': 'Greška ekstenzije datoteke.',
'File size error.': 'Greška veličine datoteke.',
'Init error.': 'Greška inicijalizacije.',
'HTTP Error.': 'HTTP greška.',
'Security error.': 'Sigurnosna greška.',
'Generic error.': 'Generička greška.',
'IO error.': 'I/O greška.',
'Stop Upload': 'Zaustavi upload.',
'Add Files': 'Dodaj datoteke',
'Start Upload': 'Pokreni upload.',
'%d files queued': '%d datoteka na čekanju.'
});
+33
View File
@@ -0,0 +1,33 @@
// Hungarian
plupload.addI18n({
'Select files' : 'Fájlok kiválasztása',
'Add files to the upload queue and click the start button.' : 'Válaszd ki a fájlokat, majd kattints az Indítás gombra.',
'Filename' : 'Fájlnév',
'Status' : 'Állapot',
'Size' : 'Méret',
'Add files' : 'Hozzáadás',
'Stop current upload' : 'Jelenlegi feltöltés megszakítása',
'Start uploading queue' : 'Várakozási sor feltöltésének indítása',
'Uploaded %d/%d files': 'Feltöltött fájlok: %d/%d',
'N/A': 'Nem elérhető',
'Drag files here.' : 'Húzd ide a fájlokat.',
'Stop upload': 'Feltöltés megszakítása',
'Start upload': 'Indítás',
'%d files queued': '%d fájl sorbaállítva',
'File: %s': 'Fájl: %s',
'Close': 'Bezárás',
'Using runtime: ': 'Használt runtime: ',
'File: %f, size: %s, max file size: %m': 'Fájl: %f, méret: %s, maximális fájlméret: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'A feltöltés egyszerre csak %d fájlt fogad el, a többi fájl nem lesz feltöltve.',
'Upload URL might be wrong or doesn\'t exist': 'A megadott URL hibás vagy nem létezik',
'Error: File too large: ': 'Hiba: A fájl túl nagy: ',
'Error: Invalid file extension: ': 'Hiba: Érvénytelen fájlkiterjesztés: ',
'File extension error.': 'Hibás fájlkiterjesztés.',
'File size error.': 'Hibás fájlméret.',
'File count error.': 'A fájlok számával kapcsolatos hiba.',
'Init error.': 'Init hiba.',
'HTTP Error.': 'HTTP hiba.',
'Security error.': 'Biztonsági hiba.',
'Generic error.': 'Általános hiba.',
'IO error.': 'I/O hiba.'
});
+24
View File
@@ -0,0 +1,24 @@
// Italian
plupload.addI18n({
'Select files' : 'Seleziona i files',
'Add files to the upload queue and click the start button.' : 'Aggiungi i file alla coda di caricamento e clicca il pulsante di avvio.',
'Filename' : 'Nome file',
'Status' : 'Stato',
'Size' : 'Dimensione',
'Add Files' : 'Aggiungi file',
'Stop current upload' : 'Interrompi il caricamento',
'Start uploading queue' : 'Avvia il caricamento',
'Uploaded %d/%d files': 'Caricati %d/%d file',
'N/A' : 'N/D',
'Drag files here.' : 'Trascina i file qui.',
'File extension error.': 'Errore estensione file.',
'File size error.': 'Errore dimensione file.',
'Init error.': 'Errore inizializzazione.',
'HTTP Error.': 'Errore HTTP.',
'Security error.': 'Errore sicurezza.',
'Generic error.': 'Errore generico.',
'IO error.': 'Errore IO.',
'Stop Upload': 'Ferma Upload',
'Start Upload': 'Inizia Upload',
'%d files queued': '%d file in lista'
});
+37
View File
@@ -0,0 +1,37 @@
// Japanese
plupload.addI18n({
'Select files' : 'ファイル選択',
'Add files to the upload queue and click the start button.' : 'ファイルをアップロードキューに追加してスタートボタンをクリックしてください',
'Filename' : 'ファイル名',
'Status' : 'ステータス',
'Size' : 'サイズ',
'Add Files' : 'ファイルを追加',
'Stop Upload' : 'アップロード停止',
'Start Upload' : 'アップロード',
'Add files' : 'ファイルを追加',
'Add files.' : 'ファイルを追加',
'Stop current upload' : '現在のアップロードを停止',
'Start uploading queue' : 'アップロード',
'Stop upload' : 'アップロード停止',
'Start upload' : 'アップロード',
'Uploaded %d/%d files': 'アップロード中 %d/%d ファイル',
'N/A' : 'N/A',
'Drag files here.' : 'ここにファイルをドラッグ',
'File extension error.': 'ファイル拡張子エラー',
'File size error.': 'ファイルサイズエラー',
'File count error.': 'ファイル数エラー',
'Init error.': 'イニシャライズエラー',
'HTTP Error.': 'HTTP エラー',
'Security error.': 'セキュリティエラー',
'Generic error.': 'エラー',
'IO error.': 'IO エラー',
'File: %s': 'ファイル: %s',
'Close': '閉じる',
'%d files queued': '%d ファイルが追加されました',
'Using runtime: ': 'モード: ',
'File: %f, size: %s, max file size: %m': 'ファイル: %f, サイズ: %s, 最大ファイルサイズ: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'アップロード可能なファイル数は %d です。余分なファイルは削除されました',
'Upload URL might be wrong or doesn\'t exist': 'アップロード先の URL が存在しません',
'Error: File too large: ': 'エラー: サイズが大きすぎます: ',
'Error: Invalid file extension: ': 'エラー: 拡張子が許可されていません: '
});
+36
View File
@@ -0,0 +1,36 @@
// Republic of Korea
plupload.addI18n({
'Select files' : '파일 선택',
'Add files to the upload queue and click the start button.' : '파일을 업로드 큐에 추가하여 시작 버튼을 클릭하십시오.',
'Filename' : '파일 이름',
'Status' : '상태',
'Size' : '크기',
'Add Files' : '파일 추가',
'Stop Upload': '업로드 중지',
'Start Upload': '업로드',
'Add files': '파일 추가',
'Stop current upload': '현재 업로드를 정지',
'Start uploading queue': '업로드',
'Stop upload': '업로드 중지',
'Start upload': '업로드',
'Uploaded % d / % d files': '업로드 중 % d / % d 파일',
'N / A': 'N / A',
'Drag files here': '여기에 파일을 드래그',
'File extension error': '파일 확장자 오류',
'File size error': '파일 크기 오류',
'File count error': '이미지 : 오류',
'Init error': '초기화 오류',
'HTTP Error': 'HTTP 오류',
'Security error': '보안 오류',
'Generic error': '오류',
'IO error': 'IO 오류',
'File : % s': '파일 % s',
'Close': '닫기',
'% d files queued': '% d 파일이 추가되었습니다',
'Using runtime :': '모드',
'File : % f, size : % s, max file size : % m': '파일 : % f, 크기 : % s, 최대 파일 크기 : % m',
'Upload element accepts only % d file (s) at a time. Extra files were stripped': '업로드 가능한 파일의 수는 % d입니다. 불필요한 파일은 삭제되었습니다 ',
'Upload URL might be wrong or doesn \'t exist ':'업로드할 URL이 존재하지 않습니다 ',
'Error : File too large :': '오류 : 크기가 너무 큽니다',
'Error : Invalid file extension :': '오류 : 확장자가 허용되지 않습니다 :'
});
+33
View File
@@ -0,0 +1,33 @@
// .lv file like language pack
plupload.addI18n({
'Select files' : 'Izvēlieties failus',
'Add files to the upload queue and click the start button.' : 'Pieveinojiet failus rindai un klikšķiniet uz "Sākt augšupielādi" pogas.',
'Filename' : 'Faila nosaukums',
'Status' : 'Statuss',
'Size' : 'Izmērs',
'Add files' : 'Pievienot failus',
'Stop current upload' : 'Apturēt pašreizējo augšupielādi',
'Start uploading queue' : 'Sākt augšupielādi',
'Drag files here.' : 'Ievelciet failus šeit',
'Start upload' : 'Sākt augšupielādi',
'Uploaded %d/%d files': 'Augšupielādēti %d/%d faili',
'Stop upload': 'Pārtraukt augšupielādi',
'Start upload': 'Sākt augšupielādi',
'%d files queued': '%d faili pievienoti rindai',
'File: %s': 'Fails: %s',
'Close': 'Aizvērt',
'Using runtime: ': 'Lieto saskarni: ',
'File: %f, size: %s, max file size: %m': 'Fails: %f, izmērs: %s, maksimālais faila izmērs: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Iespējams ielādēt tikai %d failus vienā reizē. Atlikušie faili netika pievienoti',
'Upload URL might be wrong or doesn\'t exist': 'Augšupielādes URL varētu būt nepareizs vai neeksistē',
'Error: File too large: ': 'Kļūda: Fails pārāk liels: ',
'Error: Invalid file extension: ': 'Kļūda: Nekorekts faila paplašinājums:',
'File extension error.': 'Faila paplašinājuma kļūda.',
'File size error.': 'Faila izmēra kļūda.',
'File count error.': 'Failu skaita kļūda',
'Init error.': 'Inicializācijas kļūda.',
'HTTP Error.': 'HTTP kļūda.',
'Security error.': 'Drošības kļūda.',
'Generic error.': 'Vispārēja rakstura kļūda.',
'IO error.': 'Ievades/Izvades kļūda.'
});
+21
View File
@@ -0,0 +1,21 @@
// Dutch
plupload.addI18n({
'Select files' : 'Selecteer bestand(en):',
'Add files to the upload queue and click the start button.' : 'Voeg bestanden toe aan de wachtrij en druk op \'Start\'.',
'Filename' : 'Bestandsnaam',
'Status' : 'Status',
'Size' : 'Grootte',
'Add files' : 'Voeg bestanden toe',
'Stop current upload' : 'Stop upload',
'Start uploading queue' : 'Start upload',
'Uploaded %d/%d files': '%d/%d bestanden ge-upload',
'N/A' : 'Niet beschikbaar',
'Drag files here.' : 'Sleep bestanden hierheen.',
'File extension error.': 'Ongeldig bestandstype.',
'File size error.': 'Bestandsgrootte Error.',
'Init error.': 'Initialisatie error.',
'HTTP Error.': 'HTTP Error.',
'Security error.': 'Beveiliging error.',
'Generic error.': 'Onbekende error.',
'IO error.': 'IO error.'
});
+24
View File
@@ -0,0 +1,24 @@
plupload.addI18n({
'Select files' : 'Wybierz pliki:',
'Add files to the upload queue and click the start button.' : 'Dodaj pliki i kliknij \'Rozpocznij transfer\'.',
'Filename' : 'Nazwa pliku',
'Status' : 'Status',
'Size' : 'Rozmiar',
'Add files' : 'Dodaj pliki',
'Stop current upload' : 'Przerwij aktualny transfer',
'Start uploading queue' : 'Rozpocznij wysyłanie',
'Uploaded %d/%d files': 'Wysłano %d/%d plików',
'N/A' : 'Nie dostępne',
'Drag files here.' : 'Przeciągnij tu pliki',
'File extension error.': 'Nieobsługiwany format pliku.',
'File size error.': 'Plik jest zbyt duży.',
'Init error.': 'Błąd inicjalizacji.',
'HTTP Error.': 'Błąd HTTP.',
'Security error.': 'Błąd bezpieczeństwa.',
'Generic error.': 'Błąd ogólny.',
'IO error.': 'Błąd IO.',
'Stop Upload': 'Przerwij transfer.',
'Add Files': 'Dodaj pliki',
'Start upload': 'Rozpocznij transfer.',
'%d files queued': '%d plików w kolejce.'
});
+35
View File
@@ -0,0 +1,35 @@
// Brazilian Portuguese
plupload.addI18n({
'Select files' : 'Escolha os arquivos',
'Add files to the upload queue and click the start button.' : 'Adicione os arquivos abaixo e clique no botão "Iniciar o envio".',
'Filename' : 'Nome do arquivo',
'Status' : 'Status',
'Size' : 'Tamanho',
'Add Files' : 'Adicionar arquivo(s)',
'Stop Upload' : 'Parar o envio',
'Start Upload' : 'Iniciar o envio',
'Add files' : 'Adicionar arquivo(s)',
'Add files.' : 'Adicionar arquivo(s)',
'Stop upload' : 'Parar o envio',
'Start upload' : 'Iniciar o envio',
'Uploaded %d/%d files': 'Enviado(s) %d/%d arquivo(s)',
'N/A' : 'N/D',
'Drag files here.' : 'Arraste os arquivos pra cá',
'File extension error.': 'Tipo de arquivo não permitido.',
'File size error.': 'Tamanho de arquivo não permitido.',
'File count error.': 'Erro na contagem dos arquivos',
'Init error.': 'Erro inicializando.',
'HTTP Error.': 'Erro HTTP.',
'Security error.': 'Erro de segurança.',
'Generic error.': 'Erro genérico.',
'IO error.': 'Erro de E/S.',
'File: %s': 'Arquivo: %s',
'Close': 'Fechar',
'%d files queued': '%d arquivo(s)',
'Using runtime: ': 'Usando: ',
'File: %f, size: %s, max file size: %m': 'Arquivo: %f, tamanho: %s, máximo: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Só são aceitos %d arquivos por vez. O que passou disso foi descartado.',
'Upload URL might be wrong or doesn\'t exist': 'URL de envio está errada ou não existe',
'Error: File too large: ': 'Erro: Arquivo muito grande: ',
'Error: Invalid file extension: ': 'Erro: Tipo de arquivo não permitido: '
});
+24
View File
@@ -0,0 +1,24 @@
// Romanian
plupload.addI18n({
'Select files' : 'Selectare fişiere',
'Add files to the upload queue and click the start button.' : 'Adaugă fişiere în lista apoi apasă butonul \'Începe încărcare\'.',
'Filename' : 'Nume fişier',
'Status' : 'Stare',
'Size' : 'Mărime',
'Add files' : 'Adăugare fişiere',
'Stop current upload' : 'Întrerupe încărcarea curentă',
'Start uploading queue' : 'Începe incărcarea',
'Uploaded %d/%d files': 'Fişiere încărcate %d/%d',
'N/A' : 'N/A',
'Drag files here.' : 'Trage aici fişierele',
'File extension error.': 'Extensie fişier eronată',
'File size error.': 'Eroare dimensiune fişier',
'Init error.': 'Eroare iniţializare',
'HTTP Error.': 'Eroare HTTP',
'Security error.': 'Eroare securitate',
'Generic error.': 'Eroare generică',
'IO error.': 'Eroare Intrare/Ieşire',
'Stop Upload': 'Oprire încărcare',
'Start upload': 'Începe încărcare',
'%d files queued': '%d fişiere listate'
});
+21
View File
@@ -0,0 +1,21 @@
// Russian
plupload.addI18n({
'Select files' : 'Выберите файлы',
'Add files to the upload queue and click the start button.' : 'Добавьте файлы в очередь и нажмите кнопку "Загрузить файлы".',
'Filename' : 'Имя файла',
'Status' : 'Статус',
'Size' : 'Размер',
'Add files' : 'Добавить файлы',
'Stop current upload' : 'Остановить загрузку',
'Start uploading queue' : 'Загрузить файлы',
'Uploaded %d/%d files': 'Загружено %d/%d файлов',
'N/A' : 'N/D',
'Drag files here.' : 'Перетащите файлы сюда.',
'File extension error.': 'Неправильное расширение файла.',
'File size error.': 'Неправильный размер файла.',
'Init error.': 'Ошибка инициализации.',
'HTTP Error.': 'Ошибка HTTP.',
'Security error.': 'Ошибка безопасности.',
'Generic error.': 'Общая ошибка.',
'IO error.': 'Ошибка ввода-вывода.'
});
+25
View File
@@ -0,0 +1,25 @@
// .po file like language pack
plupload.addI18n({
'Select files' : 'Vyberte súbory',
'Add files to the upload queue and click the start button.' : 'Pridajte súbory do zoznamu a potom spustite nahrávanie.',
'Filename' : 'Názov súboru',
'Status' : 'Stav',
'Size' : 'Veľkosť',
'Add files' : 'Pridať súbory',
'Stop current upload' : 'Zastaviť nahrávanie',
'Start uploading queue' : 'Spustiť nahrávanie zoznamu',
'Drag files here.' : 'Sem pretiahnite súbory.',
'Start upload': 'Spustiť nahrávanie',
'Uploaded %d/%d files': 'Nahraných %d/%d súborov',
'Using runtime: ': 'K odoslaniu súborov sa použije rozhranie: ',
'N/A' : 'N/A',
'File extension error.': 'Chybný typ súboru.',
'File size error.': 'Súbor je príliš veľký.',
'Init error.': 'Chyba inicializácie.',
'HTTP Error.': 'HTTP Chyba.',
'Security error.': 'Bezpečnostná Chyba.',
'Generic error.': 'Chyba.',
'IO error.': 'IO Chyba',
'Stop Upload': 'Zastaviť nahrávanie',
'%d files queued': '%d súborov pridaných do zoznamu'
});
+14
View File
@@ -0,0 +1,14 @@
// Serbian
plupload.addI18n({
'Select files' : 'Izaberite fajlove',
'Add files to the upload queue and click the start button.' : 'Dodajte fajlove u listu i kliknite na dugme Start.',
'Filename' : 'Naziv fajla',
'Status' : 'Status',
'Size' : 'Veličina',
'Add Files' : 'Dodaj fajlove',
'Stop current upload' : 'Zaustavi upload',
'Start uploading queue' : 'Počni upload',
'Drag files here.' : 'Prevucite fajlove ovde.',
'Start Upload': 'Počni upload',
'Uploaded %d/%d files': 'Snimljeno %d/%d fajlova'
});
+12
View File
@@ -0,0 +1,12 @@
// .po file like language pack
plupload.addI18n({
'Select files' : 'Välj filer',
'Add files to the upload queue and click the start button.' : 'Lägg till filer till kön och tryck på start.',
'Filename' : 'Filnamn',
'Status' : 'Status',
'Size' : 'Storlek',
'Add files' : 'Lägg till filer',
'Stop current upload' : 'Stoppa uppladdningen',
'Start uploading queue' : 'Starta uppladdningen',
'Drag files here.' : 'Dra filer hit'
});
@@ -0,0 +1,177 @@
/*
Plupload
------------------------------------------------------------------- */
.plupload_button {
display: -moz-inline-box; /* FF < 3*/
display: inline-block;
font: normal 12px sans-serif;
text-decoration: none;
color: #42454a;
border: 1px solid #bababa;
padding: 2px 8px 3px 20px;
margin-right: 4px;
background: #f3f3f3 url('../img/buttons.png') no-repeat 0 center;
outline: 0;
/* Optional rounded corners for browsers that support it */
-moz-border-radius: 3px;
-khtml-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
.plupload_button:hover {
color: #000;
text-decoration: none;
}
.plupload_disabled, a.plupload_disabled:hover {
color: #737373;
border-color: #c5c5c5;
background: #ededed url('../img/buttons-disabled.png') no-repeat 0 center;
cursor: default;
}
.plupload_add {
background-position: -181px center;
}
.plupload_wrapper {
font: normal 11px Verdana,sans-serif;
width: 100%;
}
.plupload_container {
padding: 8px;
background: url('../img/transp50.png');
/*-moz-border-radius: 5px;*/
}
.plupload_container input {
border: 1px solid #DDD;
font: normal 11px Verdana,sans-serif;
width: 98%;
}
.plupload_header {background: #2A2C2E url('../img/backgrounds.gif') repeat-x;}
.plupload_header_content {
background: url('../img/backgrounds.gif') no-repeat 0 -317px;
min-height: 56px;
padding-left: 60px;
color: #FFF;
}
.plupload_header_title {
font: normal 18px sans-serif;
padding: 6px 0 3px;
}
.plupload_header_text {
font: normal 12px sans-serif;
}
.plupload_filelist {
margin: 0;
padding: 0;
list-style: none;
}
.plupload_scroll .plupload_filelist {
height: 185px;
background: #F5F5F5;
overflow-y: scroll;
}
.plupload_filelist li {
padding: 10px 8px;
background: #F5F5F5 url('../img/backgrounds.gif') repeat-x 0 -156px;
border-bottom: 1px solid #DDD;
}
.plupload_filelist_header, .plupload_filelist_footer {
background: #DFDFDF;
padding: 8px 8px;
color: #42454A;
}
.plupload_filelist_header {
border-top: 1px solid #EEE;
border-bottom: 1px solid #CDCDCD;
}
.plupload_filelist_footer {border-top: 1px solid #FFF; height: 22px; line-height: 20px; vertical-align: middle;}
.plupload_file_name {float: left; overflow: hidden}
.plupload_file_status {color: #777;}
.plupload_file_status span {color: #42454A;}
.plupload_file_size, .plupload_file_status, .plupload_progress {
float: right;
width: 80px;
}
.plupload_file_size, .plupload_file_status, .plupload_file_action {text-align: right;}
.plupload_filelist .plupload_file_name {width: 205px}
.plupload_file_action {
float: right;
width: 16px;
height: 16px;
margin-left: 15px;
}
.plupload_file_action * {
display: none;
width: 16px;
height: 16px;
}
li.plupload_uploading {background: #ECF3DC url('../img/backgrounds.gif') repeat-x 0 -238px;}
li.plupload_done {color:#AAA}
li.plupload_delete a {
background: url('../img/delete.gif');
}
li.plupload_failed a {
background: url('../img/error.gif');
cursor: default;
}
li.plupload_done a {
background: url('../img/done.gif');
cursor: default;
}
.plupload_progress, .plupload_upload_status {
display: none;
}
.plupload_progress_container {
margin-top: 3px;
border: 1px solid #CCC;
background: #FFF;
padding: 1px;
}
.plupload_progress_bar {
width: 0px;
height: 7px;
background: #CDEB8B;
}
.plupload_scroll .plupload_filelist_header .plupload_file_action, .plupload_scroll .plupload_filelist_footer .plupload_file_action {
margin-right: 17px;
}
/* Floats */
.plupload_clear,.plupload_clearer {clear: both;}
.plupload_clearer, .plupload_progress_bar {
display: block;
font-size: 0;
line-height: 0;
}
li.plupload_droptext {
background: transparent;
text-align: center;
vertical-align: middle;
border: 0;
line-height: 165px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Some files were not shown because too many files have changed in this diff Show More