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

Updated Prototype to 1.7 and Getid3 to 1.8.2

This commit is contained in:
Karl Vollmer 2011-01-04 11:33:02 -04:00
parent e41b367ab1
commit cf7924243e
75 changed files with 39332 additions and 24305 deletions

2520
modules/getid3/changelog.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,54 +1,43 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // //
// | This source file is subject to version 2 of the GPL license, | // extension.cache.dbm.php - part of getID3() //
// | that is bundled with this package in the file license.txt and is | // Please see readme.txt for more information //
// | available through the world-wide-web at the following url: | // ///
// | http://www.gnu.org/copyleft/gpl.html | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // This extension written by Allan Hansen <ahØartemis*dk> //
// +----------------------------------------------------------------------+ // ///
// | Authors: James Heinrich <infoØgetid3*org> | /////////////////////////////////////////////////////////////////
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | extension.cache.mysql.php |
// | MySQL Cache Extension. |
// | dependencies: getid3. |
// +----------------------------------------------------------------------+
//
// $Id: extension.cache.dbm.php,v 1.2 2006/11/02 10:47:59 ah Exp $
/** /**
* This is a caching extension for getID3(). It works the exact same * This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information very fast * way as the getID3 class, but return cached information very fast
* *
* Example: (see also demo.cache.dbm.php in /demo/) * Example:
* *
* Normal getID3 usage (example): * Normal getID3 usage (example):
* *
* require_once 'getid3/getid3.php'; * require_once 'getid3/getid3.php';
* $getid3 = new getid3; * $getID3 = new getID3;
* $getid3->encoding = 'UTF-8'; * $getID3->encoding = 'UTF-8';
* try { * $info1 = $getID3->analyze('file1.flac');
* $info1 = $getid3->Analyse('file1.flac'); * $info2 = $getID3->analyze('file2.wv');
* $info2 = $getid3->Analyse('file2.wv');
* ....
* *
* getID3_cached usage: * getID3_cached usage:
* *
* require_once 'getid3/getid3.php'; * require_once 'getid3/getid3.php';
* require_once 'getid3/getid3/extension.cache.mysql.php'; * require_once 'getid3/getid3/extension.cache.dbm.php';
* $getid3 = new getid3_cached_mysql('localhost', 'database', 'username', 'password'); * $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
* $getid3->encoding = 'UTF-8'; * '/tmp/getid3_cache.lock');
* try { * $getID3->encoding = 'UTF-8';
* $info1 = $getid3->analyse('file1.flac'); * $info1 = $getID3->analyze('file1.flac');
* $info2 = $getid3->analyse('file2.wv'); * $info2 = $getID3->analyze('file2.wv');
* ...
* *
* *
* Supported Cache Types * Supported Cache Types
@ -80,137 +69,152 @@
*/ */
class getid3_cached_dbm extends getid3 class getID3_cached_dbm extends getID3
{ {
public function __construct($cache_type, $dbm_filename, $lock_filename) { // 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 // Check for dba extension
if (!extension_loaded('dba')) { if (!extension_loaded('dba')) {
throw new getid3_exception('PHP is not compiled with dba support, required to use DBM style cache.'); die('PHP is not compiled with dba support, required to use DBM style cache.');
} }
if (!in_array($cache_type, dba_handlers())) { // Check for specific dba driver
throw new getid3_exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); if (function_exists('dba_handlers')) { // PHP 4.3.0+
} if (!in_array($cache_type, 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 // Create lock file if needed
if (!file_exists($lock_filename)) { if (!file_exists($lock_filename)) {
if (!touch($lock_filename)) { if (!touch($lock_filename)) {
die('failed to create lock file: ' . $lock_filename); die('failed to create lock file: '.$lock_filename);
} }
} }
// Open lock file for writing // Open lock file for writing
if (!is_writeable($lock_filename)) { if (!is_writeable($lock_filename)) {
die('lock file: ' . $lock_filename . ' is not writable'); die('lock file: '.$lock_filename.' is not writable');
} }
$this->lock = fopen($lock_filename, 'w'); $this->lock = fopen($lock_filename, 'w');
// Acquire exclusive write lock to lock file // Acquire exclusive write lock to lock file
flock($this->lock, LOCK_EX); flock($this->lock, LOCK_EX);
// Create dbm-file if needed // Create dbm-file if needed
if (!file_exists($dbm_filename)) { if (!file_exists($dbm_filename)) {
if (!touch($dbm_filename)) { if (!touch($dbm_filename)) {
die('failed to create dbm file: ' . $dbm_filename); die('failed to create dbm file: '.$dbm_filename);
} }
} }
// Try to open dbm file for writing // Try to open dbm file for writing
$this->dba = @dba_open($dbm_filename, 'w', $cache_type); $this->dba = @dba_open($dbm_filename, 'w', $cache_type);
if (!$this->dba) { if (!$this->dba) {
// Failed - create new dbm file // Failed - create new dbm file
$this->dba = dba_open($dbm_filename, 'n', $cache_type); $this->dba = dba_open($dbm_filename, 'n', $cache_type);
if (!$this->dba) { if (!$this->dba) {
die('failed to create dbm file: ' . $dbm_filename); die('failed to create dbm file: '.$dbm_filename);
} }
// Insert getID3 version number // Insert getID3 version number
dba_insert(getid3::VERSION, getid3::VERSION, $this->dba); dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
} }
// Init misc values // Init misc values
$this->cache_type = $cache_type; $this->cache_type = $cache_type;
$this->dbm_filename = $dbm_filename; $this->dbm_filename = $dbm_filename;
// Register destructor // Register destructor
register_shutdown_function(array($this, '__destruct')); register_shutdown_function(array($this, '__destruct'));
// Check version number and clear cache if changed // Check version number and clear cache if changed
if (dba_fetch(getid3::VERSION, $this->dba) != getid3::VERSION) { if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) {
$this->clear_cache(); $this->clear_cache();
} }
parent::__construct(); parent::getID3();
} }
public function __destruct() { // public: destuctor
function __destruct() {
// Close dbm file // Close dbm file
@dba_close($this->dba); @dba_close($this->dba);
// Release exclusive lock // Release exclusive lock
@flock($this->lock, LOCK_UN); @flock($this->lock, LOCK_UN);
// Close lock file // Close lock file
@fclose($this->lock); @fclose($this->lock);
} }
public function clear_cache() { // public: clear cache
function clear_cache() {
// Close dbm file // Close dbm file
dba_close($this->dba); dba_close($this->dba);
// Create new dbm file // Create new dbm file
$this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type); $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
if (!$this->dba) { if (!$this->dba) {
die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename); die('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
} }
// Insert getID3 version number // Insert getID3 version number
dba_insert(getid3::VERSION, getid3::VERSION, $this->dba); dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
// Reregister shutdown function // Re-register shutdown function
register_shutdown_function(array($this, '__destruct')); register_shutdown_function(array($this, '__destruct'));
} }
// public: analyze file // public: analyze file
public function Analyze($filename) { function analyze($filename) {
if (file_exists($filename)) { if (file_exists($filename)) {
// Calc key filename::mod_time::size - should be unique // Calc key filename::mod_time::size - should be unique
$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename); $key = $filename.'::'.filemtime($filename).'::'.filesize($filename);
// Loopup key // Loopup key
$result = dba_fetch($key, $this->dba); $result = dba_fetch($key, $this->dba);
// Hit // Hit
if ($result !== false) { if ($result !== false) {
return unserialize($result); return unserialize($result);
} }
} }
// Miss // Miss
$result = parent::Analyze($filename); $result = parent::analyze($filename);
// Save result // Save result
if (file_exists($filename)) { if (file_exists($filename)) {
dba_insert($key, serialize($result), $this->dba); dba_insert($key, serialize($result), $this->dba);
} }
return $result; return $result;
} }
} }

View file

@ -1,26 +1,18 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // //
// | This source file is subject to version 2 of the GPL license, | // extension.cache.mysql.php - part of getID3() //
// | that is bundled with this package in the file license.txt and is | // Please see readme.txt for more information //
// | available through the world-wide-web at the following url: | // ///
// | http://www.gnu.org/copyleft/gpl.html | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // This extension written by Allan Hansen <ahØartemis*dk> //
// +----------------------------------------------------------------------+ // ///
// | Authors: James Heinrich <infoØgetid3*org> | /////////////////////////////////////////////////////////////////
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | extension.cache.mysql.php |
// | MySQL Cache Extension. |
// | dependencies: getid3. |
// +----------------------------------------------------------------------+
//
// $Id: extension.cache.mysql.php,v 1.2 2006/11/02 10:47:59 ah Exp $
/** /**
@ -32,23 +24,20 @@
* Normal getID3 usage (example): * Normal getID3 usage (example):
* *
* require_once 'getid3/getid3.php'; * require_once 'getid3/getid3.php';
* $getid3 = new getid3; * $getID3 = new getID3;
* $getid3->encoding = 'UTF-8'; * $getID3->encoding = 'UTF-8';
* try { * $info1 = $getID3->analyze('file1.flac');
* $info1 = $getid3->Analyse('file1.flac'); * $info2 = $getID3->analyze('file2.wv');
* $info2 = $getid3->Analyse('file2.wv');
* ....
* *
* getID3_cached usage: * getID3_cached usage:
* *
* require_once 'getid3/getid3.php'; * require_once 'getid3/getid3.php';
* require_once 'getid3/getid3/extension.cache.mysql.php'; * require_once 'getid3/getid3/extension.cache.mysql.php';
* $getid3 = new getid3_cached_mysql('localhost', 'database', 'username', 'password'); * $getID3 = new getID3_cached_mysql('localhost', 'database',
* $getid3->encoding = 'UTF-8'; * 'username', 'password');
* try { * $getID3->encoding = 'UTF-8';
* $info1 = $getid3->analyse('file1.flac'); * $info1 = $getID3->analyze('file1.flac');
* $info2 = $getid3->analyse('file2.wv'); * $info2 = $getID3->analyze('file2.wv');
* ...
* *
* *
* Supported Cache Types (this extension) * Supported Cache Types (this extension)
@ -80,98 +69,100 @@
*/ */
class getid3_cached_mysql extends getID3 class getID3_cached_mysql extends getID3
{ {
private $cursor; // private vars
private $connection; var $cursor;
var $connection;
public function __construct($host, $database, $username, $password) { // 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 // Check for mysql support
if (!function_exists('mysql_pconnect')) { if (!function_exists('mysql_pconnect')) {
throw new getid3_exception('PHP not compiled with mysql support.'); die('PHP not compiled with mysql support.');
} }
// Connect to database // Connect to database
$this->connection = @mysql_pconnect($host, $username, $password); $this->connection = mysql_pconnect($host, $username, $password);
if (!$this->connection) { if (!$this->connection) {
throw new getid3_exception('mysql_pconnect() failed - check permissions and spelling.'); die('mysql_pconnect() failed - check permissions and spelling.');
} }
// Select database // Select database
if (!@mysql_select_db($database, $this->connection)) { if (!mysql_select_db($database, $this->connection)) {
throw new getid3_exception('Cannot use database '.$database); die('Cannot use database '.$database);
} }
// Create cache table if not exists // Create cache table if not exists
$this->create_table(); $this->create_table();
// Check version number and clear cache if changed // 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); $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); list($version) = @mysql_fetch_array($this->cursor);
if ($version != getid3::VERSION) { if ($version != GETID3_VERSION) {
$this->clear_cache(); $this->clear_cache();
} }
parent::__construct(); parent::getID3();
} }
public function clear_cache() { // public: clear cache
function clear_cache() {
$this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection); $this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection);
$this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".getid3::VERSION."', -1, -1, -1, '".getid3::VERSION."')", $this->connection); $this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection);
} }
public function Analyze($filename) { // public: analyze file
function analyze($filename) {
if (file_exists($filename)) { if (file_exists($filename)) {
// Short-hands // Short-hands
$filetime = filemtime($filename); $filetime = filemtime($filename);
$filesize = filesize($filename); $filesize = filesize($filename);
$filenam2 = mysql_real_escape_string($filename, $this->connection);
// Loopup file // Loopup file
$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection); $this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".mysql_real_escape_string($filename)."') AND (`filesize` = '".mysql_real_escape_string($filesize)."') AND (`filetime` = '".mysql_real_escape_string($filetime)."')", $this->connection);
list($result) = @mysql_fetch_array($this->cursor); list($result) = @mysql_fetch_array($this->cursor);
// Hit // Hit
if ($result) { if ($result) {
return unserialize($result); return unserialize(base64_decode($result));
} }
} }
// Miss // Miss
$result = parent::Analyze($filename); $analysis = parent::analyze($filename);
// Save result // Save result
if (file_exists($filename)) { if (file_exists($filename)) {
$res2 = mysql_real_escape_string(serialize($result), $this->connection); $this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".mysql_real_escape_string($filename)."', '".mysql_real_escape_string($filesize)."', '".mysql_real_escape_string($filetime)."', '".mysql_real_escape_string(time())."', '".mysql_real_escape_string(base64_encode(serialize($analysis)))."')", $this->connection);
$this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection); }
} return $result;
return $result; }
}
// (re)create sql table // private: (re)create sql table
private function create_table($drop = false) { function create_table($drop=false) {
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` ( $this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
`filename` VARCHAR(255) NOT NULL DEFAULT '', `filename` VARCHAR(255) NOT NULL DEFAULT '',
`filesize` INT(11) NOT NULL DEFAULT '0', `filesize` INT(11) NOT NULL DEFAULT '0',
`filetime` INT(11) NOT NULL DEFAULT '0', `filetime` INT(11) NOT NULL DEFAULT '0',
`analyzetime` INT(11) NOT NULL DEFAULT '0', `analyzetime` INT(11) NOT NULL DEFAULT '0',
`value` TEXT NOT NULL, `value` TEXT NOT NULL,
PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection); PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection);
echo mysql_error($this->connection); echo mysql_error($this->connection);
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

340
modules/getid3/license.txt Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -1,297 +1,271 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.archive.gzip.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing GZIP files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> | // //
// | Allan Hansen <ahØartemis*dk> | // Module originally written by //
// +----------------------------------------------------------------------+ // Mike Mozolin <teddybearØmail*ru> //
// | module.archive.gzip.php | // //
// | module for analyzing GZIP files | /////////////////////////////////////////////////////////////////
// | dependencies: PHP compiled with zlib support (optional) |
// +----------------------------------------------------------------------+
// | Module originally written by Mike Mozolin <teddybearØmail*ru> |
// +----------------------------------------------------------------------+
//
// $Id: module.archive.gzip.php,v 1.4 2006/12/04 16:00:35 ah Exp $
class getid3_gzip {
class getid3_gzip extends getid3_handler // public: Optional file list - disable for speed.
{ var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
// public: Optional file list - disable for speed. function getid3_gzip(&$fd, &$ThisFileInfo) {
public $option_gzip_parse_contents = true; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) $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']);
// Reads the gzip-file $arr_members = explode("\x1F\x8B\x08", $buffer);
function Analyze() { 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];
$info = &$this->getid3->info; $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;
}
}
$info['fileformat'] = 'gzip'; $ThisFileInfo['gzip']['files'] = array();
$start_length = 10; $fpointer = 0;
$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os'; $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];
//|ID1|ID2|CM |FLG| MTIME |XFL|OS |
//+---+---+---+---+---+---+---+---+---+---+
@fseek($this->getid3->fp, 0); $attr = unpack($unpack_header, substr($buff, 0, $start_length));
$buffer = @fread($this->getid3->fp, $info['filesize']); $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']);
$arr_members = explode("\x1F\x8B\x08", $buffer); $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);
while (true) { $thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']);
$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)); $thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']);
if (!$this->get_os_type(ord($attr['os']))) { if (!$thisThisFileInfo['os']) {
$ThisFileInfo['error'][] = 'Read error on gzip file';
return false;
}
// Merge member with previous if wrong OS type $fpointer = 10;
$arr_members[$i - 1] .= $buf; $arr_xsubfield = array();
$arr_members[$i] = ''; // bit 2 - FLG.FEXTRA
$is_wrong_members = true; //+---+---+=================================+
continue; //| XLEN |...XLEN bytes of "extra field"...|
} //+---+---+=================================+
} if ($thisThisFileInfo['flags']['extra']) {
if (!$is_wrong_members) { $w_xlen = substr($buff, $fpointer, 2);
break; $xlen = getid3_lib::LittleEndian2Int($w_xlen);
} $fpointer += 2;
}
$fpointer = 0; $thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
$idx = 0; // Extra SubFields
for ($i = 0; $i < $num_members; $i++) { //+---+---+---+---+==================================+
if (strlen($arr_members[$i]) == 0) { //|SI1|SI2| LEN |... LEN bytes of subfield data ...|
continue; //+---+---+---+---+==================================+
} $idx = 0;
$info_gzip_member_header_idx = &$info['gzip']['member_header'][++$idx]; 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'] = preg_replace('#\.gz$#i', '', $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
$buff = "\x1F\x8B\x08".$arr_members[$i]; $thisThisFileInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
$thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
$attr = unpack($unpack_header, substr($buff, 0, $start_length)); $ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize']));
$info_gzip_member_header_idx['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']);
$info_gzip_member_header_idx['raw']['id1'] = ord($attr['cmethod']);
$info_gzip_member_header_idx['raw']['id2'] = ord($attr['cmethod']);
$info_gzip_member_header_idx['raw']['cmethod'] = ord($attr['cmethod']);
$info_gzip_member_header_idx['raw']['os'] = ord($attr['os']);
$info_gzip_member_header_idx['raw']['xflags'] = ord($attr['xflags']);
$info_gzip_member_header_idx['raw']['flags'] = ord($attr['flags']);
$info_gzip_member_header_idx['flags']['crc16'] = (bool) ($info_gzip_member_header_idx['raw']['flags'] & 0x02); if ($this->option_gzip_parse_contents) {
$info_gzip_member_header_idx['flags']['extra'] = (bool) ($info_gzip_member_header_idx['raw']['flags'] & 0x04); // Try to inflate GZip
$info_gzip_member_header_idx['flags']['filename'] = (bool) ($info_gzip_member_header_idx['raw']['flags'] & 0x08); $csize = 0;
$info_gzip_member_header_idx['flags']['comment'] = (bool) ($info_gzip_member_header_idx['raw']['flags'] & 0x10); $inflated = '';
$chkcrc32 = '';
if (function_exists('gzinflate')) {
$cdata = substr($buff, $fpointer);
$cdata = substr($cdata, 0, strlen($cdata) - 8);
$csize = strlen($cdata);
$inflated = gzinflate($cdata);
$info_gzip_member_header_idx['compression'] = $this->get_xflag_type($info_gzip_member_header_idx['raw']['xflags']); // Calculate CRC32 for inflated content
$thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']);
$info_gzip_member_header_idx['os'] = $this->get_os_type($info_gzip_member_header_idx['raw']['os']); // determine format
if (!$info_gzip_member_header_idx['os']) { $formattest = substr($inflated, 0, 32774);
$info['error'][] = 'Read error on gzip file'; $newgetID3 = new getID3();
return false; $determined_format = $newgetID3->GetFileFormat($formattest);
} unset($newgetID3);
$fpointer = 10; // file format is determined
$arr_xsubfield = array (); 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((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ini_get('upload_tmp_dir')), 'getID3')) === false) {
// can't find anywhere to create a temp file, abort
$ThisFileInfo['error'][] = 'Unable to create temp file to parse TAR inside GZIP file';
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;
// bit 2 - FLG.FEXTRA case '':
//+---+---+=================================+ default:
//| XLEN |...XLEN bytes of "extra field"...| // unknown or unhandled format
//+---+---+=================================+ break;
}
}
}
}
return true;
}
if ($info_gzip_member_header_idx['flags']['extra']) { // Converts the OS type
$w_xlen = substr($buff, $fpointer, 2); function get_os_type($key) {
$xlen = getid3_lib::LittleEndian2Int($w_xlen); static $os_type = array(
$fpointer += 2; '0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
'1' => 'Amiga',
$info_gzip_member_header_idx['raw']['xfield'] = substr($buff, $fpointer, $xlen); '2' => 'VMS (or OpenVMS)',
'3' => 'Unix',
// Extra SubFields '4' => 'VM/CMS',
//+---+---+---+---+==================================+ '5' => 'Atari TOS',
//|SI1|SI2| LEN |... LEN bytes of subfield data ...| '6' => 'HPFS filesystem (OS/2, NT)',
//+---+---+---+---+==================================+ '7' => 'Macintosh',
'8' => 'Z-System',
$idx = 0; '9' => 'CP/M',
while (true) { '10' => 'TOPS-20',
if ($idx >= $xlen) { '11' => 'NTFS filesystem (NT)',
break; '12' => 'QDOS',
} '13' => 'Acorn RISCOS',
$si1 = ord(substr($buff, $fpointer + $idx++, 1)); '255' => 'unknown'
$si2 = ord(substr($buff, $fpointer + $idx++, 1)); );
if (($si1 == 0x41) && ($si2 == 0x70)) { return @$os_type[$key];
$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
$info_gzip_member_header_idx['filename'] = preg_replace('/.gz$/', '', @$info['filename']);
if ($info_gzip_member_header_idx['flags']['filename']) {
while (true) {
if (ord($buff[$fpointer]) == 0) {
$fpointer++;
break;
}
$info_gzip_member_header_idx['filename'] .= $buff[$fpointer];
$fpointer++;
}
}
// bit 4 - FLG.FCOMMENT
//+===================================+
//|...file comment, zero-terminated...|
//+===================================+
if ($info_gzip_member_header_idx['flags']['comment']) {
while (true) {
if (ord($buff[$fpointer]) == 0) {
$fpointer++;
break;
}
$info_gzip_member_header_idx['comment'] .= $buff[$fpointer];
$fpointer++;
}
}
// bit 1 - FLG.FHCRC
//+---+---+
//| CRC16 |
//+---+---+
if ($info_gzip_member_header_idx['flags']['crc16']) {
$w_crc = substr($buff, $fpointer, 2);
$info_gzip_member_header_idx['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
$fpointer += 2;
}
// bit 0 - FLG.FTEXT
//if ($info_gzip_member_header_idx['raw']['flags'] & 0x01) {
// Ignored...
//}
// bits 5, 6, 7 - reserved
$info_gzip_member_header_idx['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
$info_gzip_member_header_idx['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
if ($this->option_gzip_parse_contents) {
// Try to inflate GZip
if (!function_exists('gzinflate')) {
$this->getid3->warning('PHP does not have zlib support - contents not parsed.');
return true;
}
$csize = 0;
$inflated = '';
$chkcrc32 = '';
$cdata = substr($buff, $fpointer);
$cdata = substr($cdata, 0, strlen($cdata) - 8);
$csize = strlen($cdata);
$inflated = gzinflate($cdata);
// Calculate CRC32 for inflated content
$info_gzip_member_header_idx['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $info_gzip_member_header_idx['crc32']);
//// Analyse contents
// write content to temp file
if (($temp_file_name = tempnam('*', 'getID3')) === false) {
throw new getid3_exception('Unable to create temporary file.');
}
if ($tmp = fopen($temp_file_name, 'wb')) {
fwrite($tmp, $inflated);
fclose($tmp);
// clone getid3 - we want same settings
$clone = clone $this->getid3;
unset($clone->info);
try {
$clone->Analyze($temp_file_name);
$info_gzip_member_header_idx['parsed_content'] = $clone->info;
}
catch (getid3_exception $e) {
// unable to parse contents
}
unlink($temp_file_name);
}
// Unknown/unhandled format
else {
}
}
}
return true;
}
// Converts the OS type
public static 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
public static function get_xflag_type($key) {
static $xflag_type = array (
'0' => 'unknown',
'2' => 'maximum compression',
'4' => 'fastest algorithm'
);
return @$xflag_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];
}
} }
?> ?>

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['filenamepath'])) {
$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;
}
}
?>

View file

@ -1,105 +1,96 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.archive.szip.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing SZIP compressed files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.archive.szip.php |
// | module for analyzing SZIP compressed files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.archive.szip.php,v 1.2 2006/11/02 10:48:00 ah Exp $
class getid3_szip
class getid3_szip extends getid3_handler
{ {
public function Analyze() { function getid3_szip(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; 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;
}
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['fileformat'] = 'szip';
$szip_rkau = fread($getid3->fp, 6);
// Magic bytes: 'SZ'."\x0A\x04" $ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
$ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
$getid3->info['fileformat'] = 'szip'; 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;
$getid3->info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($szip_rkau, 4, 1)); case 'BH':
$getid3->info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($szip_rkau, 5, 1)); $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)
while (!feof($getid3->fp)) { $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$next_block_id = fread($getid3->fp, 2); $BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
switch ($next_block_id) {
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($getid3->fp, 4, SEEK_CUR);
break;
case 'BH': $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$bh_header_bytes = getid3_lib::BigEndian2Int(fread($getid3->fp, 3)); $BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
$bh_header_data = fread($getid3->fp, $bh_header_bytes);
$bh_header_offset = 0;
while (strpos($bh_header_data, "\x00", $bh_header_offset) > 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)
$bh_data_array['filename'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, "\x00")); $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
$bh_header_offset += (strlen($bh_data_array['filename']) + 1); $BHheaderoffset += (strlen($BHdataArray['group']) + 1);
$bh_data_array['owner'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, "\x00")); $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
$bh_header_offset += (strlen($bh_data_array['owner']) + 1); $BHheaderoffset += 3;
$bh_data_array['group'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, "\x00")); $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
$bh_header_offset += (strlen($bh_data_array['group']) + 1); $BHheaderoffset += 2;
$bh_data_array['filelength'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 3)); $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$bh_header_offset += 3; $BHheaderoffset += 4;
$bh_data_array['access_flags'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 2)); $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$bh_header_offset += 2; $BHheaderoffset += 4;
$bh_data_array['creation_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4)); $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
$bh_header_offset += 4; $BHheaderoffset += 4;
$bh_data_array['modification_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4)); $ThisFileInfo['szip']['BH'][] = $BHdataArray;
$bh_header_offset += 4; }
break;
$bh_data_array['access_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4)); default:
$bh_header_offset += 4; break 2;
}
}
$getid3->info['szip']['BH'][] = $bh_data_array; return true;
}
break;
default: }
break 2;
}
}
return true;
}
} }

View file

@ -1,231 +1,174 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.archive.tar.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing TAR files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> | // //
// | Allan Hansen <ahØartemis*dk> | // Module originally written by //
// +----------------------------------------------------------------------+ // Mike Mozolin <teddybearØmail*ru> //
// | module.archive.tar.php | // //
// | module for analyzing TAR files | /////////////////////////////////////////////////////////////////
// | dependencies: NONE |
// +----------------------------------------------------------------------+
// | Module originally written by Mike Mozolin <teddybearØmail*ru> |
// +----------------------------------------------------------------------+
//
// $Id: module.archive.tar.php,v 1.2 2006/11/02 10:48:00 ah Exp $
class getid3_tar {
class getid3_tar extends getid3_handler function getid3_tar(&$fd, &$ThisFileInfo) {
{ $ThisFileInfo['fileformat'] = 'tar';
$ThisFileInfo['tar']['files'] = array();
function Analyze() { $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
$info = &$this->getid3->info; @fseek($fd, 0);
while (!feof($fd)) {
$buffer = fread($fd, 512);
if (strlen($buffer) < 512) {
break;
}
$info['fileformat'] = 'tar'; // 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;
}
$fp = $this->getid3->fp; // Read to the next chunk
fseek($fd, $size, SEEK_CUR);
fseek($fp, 0); $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;
}
$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155/prefix'; // 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
$null_512k = str_repeat("\0", 512); // end-of-file marker // 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' : '-');
$already_warned = false; // 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';
while (!feof($fp)) { $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;
}
$buffer = fread($fp, 512); // Converts the file type
function get_flag_type($typflag) {
// check the block static $flag_types = array(
$checksum = 0; '0' => 'LF_NORMAL',
for ($i = 0; $i < 148; $i++) { '1' => 'LF_LINK',
$checksum += ord(substr($buffer, $i, 1)); '2' => 'LF_SYNLINK',
} '3' => 'LF_CHR',
for ($i = 148; $i < 156; $i++) { '4' => 'LF_BLK',
$checksum += ord(' '); '5' => 'LF_DIR',
} '6' => 'LF_FIFO',
for ($i = 156; $i < 512; $i++) { '7' => 'LF_CONFIG',
$checksum += ord(substr($buffer, $i, 1)); 'D' => 'LF_DUMPDIR',
} 'K' => 'LF_LONGLINK',
$attr = unpack($unpack_header, $buffer); 'L' => 'LF_LONGNAME',
$name = trim(@$attr['fname']); 'M' => 'LF_MULTIVOL',
$mode = octdec(trim(@$attr['mode'])); 'N' => 'LF_NAMES',
$uid = octdec(trim(@$attr['uid'])); 'S' => 'LF_SPARSE',
$gid = octdec(trim(@$attr['gid'])); 'V' => 'LF_VOLHDR'
$size = octdec(trim(@$attr['size'])); );
$mtime = octdec(trim(@$attr['mtime'])); return @$flag_types[$typflag];
$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']);
// EOF Found
if (($checksum == 256) && ($chksum == 0)) {
break;
}
// Check if filename if 7bit as spec requires
if (!$already_warned) {
for ($i = 0; $i < strlen($name); $i++) {
if ($name{$i} < chr(32) || $name{$i} > chr(127)) {
$this->getid3->warning('Some filenames contains extended characters, which breaks the tar specifation. This is not uncommon, but you will have to handle the character encoding for filenames yourself.');
$already_warned = true;
break;
}
}
}
if ($prefix) {
$name = $prefix.'/'.$name;
}
if ((preg_match('#/$#', $name)) && !$name) {
$typeflag = 5;
}
// If it's the end of the tar-file...
if ($buffer == $null_512k) {
break;
}
// Protect against tar-files with garbage at the end
if ($name == '') {
break;
}
$info['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
);
// Skip the next chunk
fseek($fp, $size, SEEK_CUR);
// Throw away padding
if ($size % 512) {
fseek($fp, 512 - $diff, SEEK_CUR);
}
}
return true;
}
// Parses the file mode to file permissions
public static 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
public static 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];
}
} }

View file

@ -1,509 +1,414 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.archive.zip.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing pkZip files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.archive.zip.php |
// | Module for analyzing pkZip files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.archive.zip.php,v 1.4 2006/11/02 10:48:00 ah Exp $
class getid3_zip
class getid3_zip extends getid3_handler
{ {
public function Analyze() { function getid3_zip(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; $ThisFileInfo['fileformat'] = 'zip';
$ThisFileInfo['zip']['encoding'] = 'ISO-8859-1';
$getid3->info['zip'] = array (); $ThisFileInfo['zip']['files'] = array();
$info_zip = &$getid3->info['zip'];
$ThisFileInfo['zip']['compressed_size'] = 0;
$getid3->info['fileformat'] = 'zip'; $ThisFileInfo['zip']['uncompressed_size'] = 0;
$ThisFileInfo['zip']['entries_count'] = 0;
$info_zip['encoding'] = 'ISO-8859-1';
$info_zip['files'] = array (); if ($ThisFileInfo['filesize'] < pow(2, 31)) {
$info_zip['compressed_size'] = $info_zip['uncompressed_size'] = $info_zip['entries_count'] = 0; $EOCDsearchData = '';
$EOCDsearchCounter = 0;
$eocd_search_data = ''; while ($EOCDsearchCounter++ < 512) {
$eocd_search_counter = 0;
while ($eocd_search_counter++ < 512) { fseek($fd, -128 * $EOCDsearchCounter, SEEK_END);
$EOCDsearchData = fread($fd, 128).$EOCDsearchData;
fseek($getid3->fp, -128 * $eocd_search_counter, SEEK_END);
$eocd_search_data = fread($getid3->fp, 128).$eocd_search_data; if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
if (strstr($eocd_search_data, 'PK'."\x05\x06")) { $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
$eocd_position = strpos($eocd_search_data, 'PK'."\x05\x06"); $ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd);
fseek($getid3->fp, (-128 * $eocd_search_counter) + $eocd_position, SEEK_END);
$info_zip['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory(); fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET);
$ThisFileInfo['zip']['entries_count'] = 0;
fseek($getid3->fp, $info_zip['end_central_directory']['directory_offset'], SEEK_SET); while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
$info_zip['entries_count'] = 0; $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
while ($central_directoryentry = $this->ZIPparseCentralDirectory($getid3->fp)) { $ThisFileInfo['zip']['entries_count']++;
$info_zip['central_directory'][] = $central_directoryentry; $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
$info_zip['entries_count']++; $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
$info_zip['compressed_size'] += $central_directoryentry['compressed_size'];
$info_zip['uncompressed_size'] += $central_directoryentry['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 ($central_directoryentry['uncompressed_size'] > 0) { }
$info_zip['files'] = getid3_zip::array_merge_clobber($info_zip['files'], getid3_zip::CreateDeepArray($central_directoryentry['filename'], '/', $central_directoryentry['uncompressed_size'])); }
}
} if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
if ($info_zip['entries_count'] == 0) { return false;
throw new getid3_exception('No Central Directory entries found (truncated file?)'); }
}
if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
if (!empty($info_zip['end_central_directory']['comment'])) { $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
$info_zip['comments']['comment'][] = $info_zip['end_central_directory']['comment']; }
}
if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) {
if (isset($info_zip['central_directory'][0]['compression_method'])) { $ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method'];
$info_zip['compression_method'] = $info_zip['central_directory'][0]['compression_method']; }
} if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) {
if (isset($info_zip['central_directory'][0]['flags']['compression_speed'])) { $ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'];
$info_zip['compression_speed'] = $info_zip['central_directory'][0]['flags']['compression_speed']; }
} if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) {
if (isset($info_zip['compression_method']) && ($info_zip['compression_method'] == 'store') && !isset($info_zip['compression_speed'])) { $ThisFileInfo['zip']['compression_speed'] = 'store';
$info_zip['compression_speed'] = 'store'; }
}
return true;
return true;
} }
} }
}
if ($this->getZIPentriesFilepointer()) {
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 // central directory couldn't be found and/or parsed
if (@$info_zip['compressed_size'] > ($getid3->info['filesize'] - 46 - 22)) { // scan through actual file data entries, recover as much as possible from probable trucated file
throw new getid3_exception('Warning: Truncated file! - Total compressed file sizes ('.$info_zip['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($getid3->info['filesize'] - 46 - 22).' bytes)'); 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)';
throw new getid3_exception('Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'); }
} $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) {
//throw new getid3_exception('Cannot find End Of Central Directory (truncated file?)'); $ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
} }
return true;
} else {
private function getZIPHeaderFilepointerTopDown() {
unset($ThisFileInfo['zip']);
// shortcut $ThisFileInfo['fileformat'] = '';
$getid3 = $this->getid3; $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
return false;
$getid3->info['fileformat'] = 'zip';
}
$getid3->info['zip'] = array (); }
$info_zip['compressed_size'] = $info_zip['uncompressed_size'] = $info_zip['entries_count'] = 0;
rewind($getid3->fp); function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) {
while ($fileentry = $this->ZIPparseLocalFileHeader()) { $ThisFileInfo['fileformat'] = 'zip';
$info_zip['entries'][] = $fileentry;
$info_zip['entries_count']++; $ThisFileInfo['zip']['compressed_size'] = 0;
} $ThisFileInfo['zip']['uncompressed_size'] = 0;
if ($info_zip['entries_count'] == 0) { $ThisFileInfo['zip']['entries_count'] = 0;
throw new getid3_exception('No Local File Header entries found');
} rewind($fd);
while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
$info_zip['entries_count'] = 0; $ThisFileInfo['zip']['entries'][] = $fileentry;
while ($central_directoryentry = $this->ZIPparseCentralDirectory($getid3->fp)) { $ThisFileInfo['zip']['entries_count']++;
$info_zip['central_directory'][] = $central_directoryentry; }
$info_zip['entries_count']++; if ($ThisFileInfo['zip']['entries_count'] == 0) {
$info_zip['compressed_size'] += $central_directoryentry['compressed_size']; $ThisFileInfo['error'][] = 'No Local File Header entries found';
$info_zip['uncompressed_size'] += $central_directoryentry['uncompressed_size']; return false;
} }
if ($info_zip['entries_count'] == 0) {
throw new getid3_exception('No Central Directory entries found (truncated file?)'); $ThisFileInfo['zip']['entries_count'] = 0;
} while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
$ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
if ($eocd = $this->ZIPparseEndOfCentralDirectory()) { $ThisFileInfo['zip']['entries_count']++;
$info_zip['end_central_directory'] = $eocd; $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
} else { $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
throw new getid3_exception('No End Of Central Directory entry found (truncated file?)'); }
} if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
if (!@$info_zip['end_central_directory']['comment']) { return false;
$info_zip['comments']['comment'][] = $info_zip['end_central_directory']['comment']; }
}
if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) {
return true; $ThisFileInfo['zip']['end_central_directory'] = $EOCD;
} } else {
$ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)';
return false;
}
private function getZIPentriesFilepointer() {
if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
// shortcut $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
$getid3 = $this->getid3; }
$getid3->info['zip'] = array (); return true;
$info_zip['compressed_size'] = $info_zip['uncompressed_size'] = $info_zip['entries_count'] = 0; }
rewind($getid3->fp);
while ($fileentry = $this->ZIPparseLocalFileHeader($getid3->fp)) { function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) {
$info_zip['entries'][] = $fileentry; $ThisFileInfo['zip']['compressed_size'] = 0;
$info_zip['entries_count']++; $ThisFileInfo['zip']['uncompressed_size'] = 0;
$info_zip['compressed_size'] += $fileentry['compressed_size']; $ThisFileInfo['zip']['entries_count'] = 0;
$info_zip['uncompressed_size'] += $fileentry['uncompressed_size'];
} rewind($fd);
if ($info_zip['entries_count'] == 0) { while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
throw new getid3_exception('No Local File Header entries found'); $ThisFileInfo['zip']['entries'][] = $fileentry;
} $ThisFileInfo['zip']['entries_count']++;
$ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size'];
return true; $ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
} }
if ($ThisFileInfo['zip']['entries_count'] == 0) {
$ThisFileInfo['error'][] = 'No Local File Header entries found';
return false;
private function ZIPparseLocalFileHeader() { }
// shortcut return true;
$getid3 = $this->getid3; }
$local_file_header['offset'] = ftell($getid3->fp);
function ZIPparseLocalFileHeader(&$fd) {
$zip_local_file_header = fread($getid3->fp, 30); $LocalFileHeader['offset'] = ftell($fd);
$local_file_header['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($zip_local_file_header, 0, 4)); $ZIPlocalFileHeader = fread($fd, 30);
// Invalid Local File Header Signature $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
if ($local_file_header['raw']['signature'] != 0x04034B50) { if ($LocalFileHeader['raw']['signature'] != 0x04034B50) {
fseek($getid3->fp, $local_file_header['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly // invalid Local File Header Signature
return false; fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
} return false;
}
getid3_lib::ReadSequence('LittleEndian2Int', $local_file_header['raw'], $zip_local_file_header, 4, $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
array ( $LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2));
'extract_version' => 2, $LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2));
'general_flags' => 2, $LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
'compression_method' => 2, $LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
'last_mod_file_time' => 2, $LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
'last_mod_file_date' => 2, $LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
'crc_32' => 2, $LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
'compressed_size' => 2, $LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
'uncompressed_size' => 2, $LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
'filename_length' => 2,
'extra_field_length' => 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'];
$local_file_header['extract_version'] = sprintf('%1.1f', $local_file_header['raw']['extract_version'] / 10); $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size'];
$local_file_header['host_os'] = $this->ZIPversionOSLookup(($local_file_header['raw']['extract_version'] & 0xFF00) >> 8); $LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
$local_file_header['compression_method'] = $this->ZIPcompressionMethodLookup($local_file_header['raw']['compression_method']); $LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
$local_file_header['compressed_size'] = $local_file_header['raw']['compressed_size'];
$local_file_header['uncompressed_size'] = $local_file_header['raw']['uncompressed_size']; $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
$local_file_header['flags'] = $this->ZIPparseGeneralPurposeFlags($local_file_header['raw']['general_flags'], $local_file_header['raw']['compression_method']); if ($FilenameExtrafieldLength > 0) {
$local_file_header['last_modified_timestamp'] = $this->DOStime2UNIXtime($local_file_header['raw']['last_mod_file_date'], $local_file_header['raw']['last_mod_file_time']); $ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength);
$filename_extra_field_length = $local_file_header['raw']['filename_length'] + $local_file_header['raw']['extra_field_length']; if ($LocalFileHeader['raw']['filename_length'] > 0) {
if ($filename_extra_field_length > 0) { $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
$zip_local_file_header .= fread($getid3->fp, $filename_extra_field_length); }
if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
if ($local_file_header['raw']['filename_length'] > 0) { $LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
$local_file_header['filename'] = substr($zip_local_file_header, 30, $local_file_header['raw']['filename_length']); }
} }
if ($local_file_header['raw']['extra_field_length'] > 0) {
$local_file_header['raw']['extra_field_data'] = substr($zip_local_file_header, 30 + $local_file_header['raw']['filename_length'], $local_file_header['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);
$local_file_header['data_offset'] = ftell($getid3->fp); if ($LocalFileHeader['flags']['data_descriptor_used']) {
fseek($getid3->fp, $local_file_header['raw']['compressed_size'], SEEK_CUR); $DataDescriptor = fread($fd, 12);
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
if ($local_file_header['flags']['data_descriptor_used']) { $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
$data_descriptor = fread($getid3->fp, 12); $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
}
getid3_lib::ReadSequence('LittleEndian2Int', $local_file_header['data_descriptor'], $data_descriptor, 0,
array ( return $LocalFileHeader;
'crc_32' => 4, }
'compressed_size' => 4,
'uncompressed_size' => 4
) function ZIPparseCentralDirectory(&$fd) {
); $CentralDirectory['offset'] = ftell($fd);
}
$ZIPcentralDirectory = fread($fd, 46);
return $local_file_header;
} $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
private function ZIPparseCentralDirectory() { return false;
}
// shortcut $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
$getid3 = $this->getid3; $CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2));
$CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2));
$central_directory['offset'] = ftell($getid3->fp); $CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
$CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
$zip_central_directory = fread($getid3->fp, 46); $CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
$CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
$central_directory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($zip_central_directory, 0, 4)); $CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
$CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
// invalid Central Directory Signature $CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
if ($central_directory['raw']['signature'] != 0x02014B50) { $CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
fseek($getid3->fp, $central_directory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly $CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
return false; $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));
getid3_lib::ReadSequence('LittleEndian2Int', $central_directory['raw'], $zip_central_directory, 4, $CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
array (
'create_version' => 2, $CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset'];
'extract_version' => 2, $CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
'general_flags' => 2, $CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
'compression_method' => 2, $CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
'last_mod_file_time' => 2, $CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
'last_mod_file_date' => 2, $CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size'];
'crc_32' => 4, $CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size'];
'compressed_size' => 4, $CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
'uncompressed_size' => 4, $CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
'filename_length' => 2,
'extra_field_length' => 2, $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
'file_comment_length' => 2, if ($FilenameExtrafieldCommentLength > 0) {
'disk_number_start' => 2, $FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength);
'internal_file_attrib' => 2,
'external_file_attrib' => 4, if ($CentralDirectory['raw']['filename_length'] > 0) {
'local_header_offset' => 4 $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']);
$central_directory['entry_offset'] = $central_directory['raw']['local_header_offset']; }
$central_directory['create_version'] = sprintf('%1.1f', $central_directory['raw']['create_version'] / 10); if ($CentralDirectory['raw']['file_comment_length'] > 0) {
$central_directory['extract_version'] = sprintf('%1.1f', $central_directory['raw']['extract_version'] / 10); $CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
$central_directory['host_os'] = $this->ZIPversionOSLookup(($central_directory['raw']['extract_version'] & 0xFF00) >> 8); }
$central_directory['compression_method'] = $this->ZIPcompressionMethodLookup($central_directory['raw']['compression_method']); }
$central_directory['compressed_size'] = $central_directory['raw']['compressed_size'];
$central_directory['uncompressed_size'] = $central_directory['raw']['uncompressed_size']; return $CentralDirectory;
$central_directory['flags'] = $this->ZIPparseGeneralPurposeFlags($central_directory['raw']['general_flags'], $central_directory['raw']['compression_method']); }
$central_directory['last_modified_timestamp'] = $this->DOStime2UNIXtime($central_directory['raw']['last_mod_file_date'], $central_directory['raw']['last_mod_file_time']);
function ZIPparseEndOfCentralDirectory(&$fd) {
$filename_extra_field_comment_length = $central_directory['raw']['filename_length'] + $central_directory['raw']['extra_field_length'] + $central_directory['raw']['file_comment_length']; $EndOfCentralDirectory['offset'] = ftell($fd);
if ($filename_extra_field_comment_length > 0) {
$filename_extra_field_comment = fread($getid3->fp, $filename_extra_field_comment_length); $ZIPendOfCentralDirectory = fread($fd, 22);
if ($central_directory['raw']['filename_length'] > 0) { $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
$central_directory['filename']= substr($filename_extra_field_comment, 0, $central_directory['raw']['filename_length']); if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
} // invalid End Of Central Directory Signature
if ($central_directory['raw']['extra_field_length'] > 0) { fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
$central_directory['raw']['extra_field_data'] = substr($filename_extra_field_comment, $central_directory['raw']['filename_length'], $central_directory['raw']['extra_field_length']); return false;
} }
if ($central_directory['raw']['file_comment_length'] > 0) { $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
$central_directory['file_comment'] = substr($filename_extra_field_comment, $central_directory['raw']['filename_length'] + $central_directory['raw']['extra_field_length'], $central_directory['raw']['file_comment_length']); $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));
return $central_directory; $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']);
private function ZIPparseEndOfCentralDirectory() { }
// shortcut return $EndOfCentralDirectory;
$getid3 = $this->getid3; }
$end_of_central_directory['offset'] = ftell($getid3->fp);
function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
$zip_end_of_central_directory = fread($getid3->fp, 22); $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
$end_of_central_directory['signature'] = getid3_lib::LittleEndian2Int(substr($zip_end_of_central_directory, 0, 4)); switch ($compressionmethod) {
case 6:
// invalid End Of Central Directory Signature $ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096);
if ($end_of_central_directory['signature'] != 0x06054B50) { $ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2);
fseek($getid3->fp, $end_of_central_directory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly break;
return false;
} case 8:
case 9:
getid3_lib::ReadSequence('LittleEndian2Int', $end_of_central_directory, $zip_end_of_central_directory, 4, switch (($flagbytes & 0x0006) >> 1) {
array ( case 0:
'disk_number_current' => 2, $ParsedFlags['compression_speed'] = 'normal';
'disk_number_start_directory' => 2, break;
'directory_entries_this_disk' => 2, case 1:
'directory_entries_total' => 2, $ParsedFlags['compression_speed'] = 'maximum';
'directory_size' => 4, break;
'directory_offset' => 4, case 2:
'comment_length' => 2 $ParsedFlags['compression_speed'] = 'fast';
) break;
); case 3:
$ParsedFlags['compression_speed'] = 'superfast';
if ($end_of_central_directory['comment_length'] > 0) { break;
$end_of_central_directory['comment'] = fread($getid3->fp, $end_of_central_directory['comment_length']); }
} break;
}
return $end_of_central_directory; $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
}
return $ParsedFlags;
}
public static function ZIPparseGeneralPurposeFlags($flag_bytes, $compression_method) {
function ZIPversionOSLookup($index) {
$parsed_flags['encrypted'] = (bool)($flag_bytes & 0x0001); static $ZIPversionOSLookup = array(
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
switch ($compression_method) { 1 => 'Amiga',
case 6: 2 => 'OpenVMS',
$parsed_flags['dictionary_size'] = (($flag_bytes & 0x0002) ? 8192 : 4096); 3 => 'Unix',
$parsed_flags['shannon_fano_trees'] = (($flag_bytes & 0x0004) ? 3 : 2); 4 => 'VM/CMS',
break; 5 => 'Atari ST',
6 => 'OS/2 H.P.F.S.',
case 8: 7 => 'Macintosh',
case 9: 8 => 'Z-System',
switch (($flag_bytes & 0x0006) >> 1) { 9 => 'CP/M',
case 0: 10 => 'Windows NTFS',
$parsed_flags['compression_speed'] = 'normal'; 11 => 'MVS',
break; 12 => 'VSE',
case 1: 13 => 'Acorn Risc',
$parsed_flags['compression_speed'] = 'maximum'; 14 => 'VFAT',
break; 15 => 'Alternate MVS',
case 2: 16 => 'BeOS',
$parsed_flags['compression_speed'] = 'fast'; 17 => 'Tandem'
break; );
case 3:
$parsed_flags['compression_speed'] = 'superfast'; return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
break; }
}
break; function ZIPcompressionMethodLookup($index) {
} static $ZIPcompressionMethodLookup = array(
$parsed_flags['data_descriptor_used'] = (bool)($flag_bytes & 0x0008); 0 => 'store',
1 => 'shrink',
return $parsed_flags; 2 => 'reduce-1',
} 3 => 'reduce-2',
4 => 'reduce-3',
5 => 'reduce-4',
6 => 'implode',
public static function ZIPversionOSLookup($index) { 7 => 'tokenize',
8 => 'deflate',
static $lookup = array ( 9 => 'deflate64',
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', 10 => 'PKWARE Date Compression Library Imploding'
1 => 'Amiga', );
2 => 'OpenVMS',
3 => 'Unix', return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
4 => 'VM/CMS', }
5 => 'Atari ST',
6 => 'OS/2 H.P.F.S.', function DOStime2UNIXtime($DOSdate, $DOStime) {
7 => 'Macintosh', // wFatDate
8 => 'Z-System', // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
9 => 'CP/M', // Bits Contents
10 => 'Windows NTFS', // 0-4 Day of the month (1-31)
11 => 'MVS', // 5-8 Month (1 = January, 2 = February, and so on)
12 => 'VSE', // 9-15 Year offset from 1980 (add 1980 to get actual year)
13 => 'Acorn Risc',
14 => 'VFAT', $UNIXday = ($DOSdate & 0x001F);
15 => 'Alternate MVS', $UNIXmonth = (($DOSdate & 0x01E0) >> 5);
16 => 'BeOS', $UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980;
17 => 'Tandem'
); // wFatTime
return (isset($lookup[$index]) ? $lookup[$index] : '[unknown]'); // 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)
public static function ZIPcompressionMethodLookup($index) {
$UNIXsecond = ($DOStime & 0x001F) * 2;
static $lookup = array ( $UNIXminute = (($DOStime & 0x07E0) >> 5);
0 => 'store', $UNIXhour = (($DOStime & 0xF800) >> 11);
1 => 'shrink',
2 => 'reduce-1', return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
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($lookup[$index]) ? $lookup[$index] : '[unknown]');
}
public static function DOStime2UNIXtime($DOSdate, $DOStime) {
/*
// wFatDate
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
// Bits Contents
// 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);
*/
return gmmktime(($DOStime & 0xF800) >> 11, ($DOStime & 0x07E0) >> 5, ($DOStime & 0x001F) * 2, ($DOSdate & 0x01E0) >> 5, $DOSdate & 0x001F, (($DOSdate & 0xFE00) >> 9) + 1980);
}
public static function array_merge_clobber($array1, $array2) {
// written by kcØhireability*com
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if (!is_array($array1) || !is_array($array2)) {
return false;
}
$newarray = $array1;
foreach ($array2 as $key => $val) {
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
$newarray[$key] = getid3_zip::array_merge_clobber($newarray[$key], $val);
} else {
$newarray[$key] = $val;
}
}
return $newarray;
}
public static function CreateDeepArray($array_path, $separator, $value) {
// assigns $value to a nested array path:
// $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt')
// is the same as:
// $foo = array ('path'=>array('to'=>'array('my'=>array('file.txt'))));
// or
// $foo['path']['to']['my'] = 'file.txt';
while ($array_path{0} == $separator) {
$array_path = substr($array_path, 1);
}
if (($pos = strpos($array_path, $separator)) !== false) {
return array (substr($array_path, 0, $pos) => getid3_zip::CreateDeepArray(substr($array_path, $pos + 1), $separator, $value));
}
return array ($array_path => $value);
}
} }

File diff suppressed because it is too large Load diff

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

View file

@ -1,188 +1,266 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | // //
// +----------------------------------------------------------------------+ // FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
// | This source file is subject to version 2 of the GPL license, | // //
// | that is bundled with this package in the file license.txt and is | // * version 0.1 (26 June 2005) //
// | available through the world-wide-web at the following url: | // //
// | http://www.gnu.org/copyleft/gpl.html | // minor modifications by James Heinrich <info@getid3.org> //
// +----------------------------------------------------------------------+ // * version 0.1.1 (15 July 2005) //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // //
// +----------------------------------------------------------------------+ // Support for On2 VP6 codec and meta information //
// | Authors: James Heinrich <infoØgetid3*org> | // by Steve Webster <steve.webster@featurecreep.com> //
// | Allan Hansen <ahØartemis*dk> | // * version 0.2 (22 February 2006) //
// +----------------------------------------------------------------------+ // //
// | module.archive.gzip.php | // Modified to not read entire file into memory //
// | module for analyzing GZIP files | // by James Heinrich <info@getid3.org> //
// | dependencies: NONE | // * version 0.3 (15 June 2006) //
// +----------------------------------------------------------------------+ // //
// | FLV module by Seth Kaufman <sethØwhirl-i.gig*com> | // Bugfixes for incorrectly parsed FLV dimensions //
// | | // and incorrect parsing of onMetaTag //
// | * version 0.1 (26 June 2005) | // by Evgeny Moysevich <moysevich@gmail.com> //
// | | // * version 0.4 (07 December 2007) //
// | minor modifications by James Heinrich <infoØgetid3*org> | // //
// | * version 0.1.1 (15 July 2005) | // Fixed parsing of audio tags and added additional codec //
// | | // details. The duration is now read from onMetaTag (if //
// | Support for On2 VP6 codec and meta information by | // exists), rather than parsing whole file //
// | Steve Webster <steve.websterØfeaturecreep*com> | // by Nigel Barnes <ngbarnes@hotmail.com> //
// | * version 0.2 (22 February 2006) | // * version 0.5 (21 May 2009) //
// | | // //
// | Modified to not read entire file into memory | // Better parsing of files with h264 video //
// | by James Heinrich <infoØgetid3*org> | // by Evgeny Moysevich <moysevichØgmail*com> //
// | * version 0.3 (15 June 2006) | // * version 0.6 (24 May 2009) //
// | | // //
// | Modifications by Allan Hansen <ahØartemis*dk> | /////////////////////////////////////////////////////////////////
// | Adapted module for PHP5 and getID3 2.0.0. | // //
// +----------------------------------------------------------------------+ // module.audio-video.flv.php //
// // module for analyzing Shockwave Flash Video files //
// $Id: module.audio-video.flv.php,v 1.7 2006/11/10 11:20:12 ah Exp $ // dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
define('GETID3_FLV_TAG_AUDIO', 8);
define('GETID3_FLV_TAG_VIDEO', 9);
define('GETID3_FLV_TAG_META', 18);
define('GETID3_FLV_VIDEO_H263', 2);
define('GETID3_FLV_VIDEO_SCREEN', 3);
define('GETID3_FLV_VIDEO_VP6FLV', 4);
define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
define('GETID3_FLV_VIDEO_SCREENV2', 6);
define('GETID3_FLV_VIDEO_H264', 7);
class getid3_flv extends getid3_handler define('H264_AVC_SEQUENCE_HEADER', 0);
define('H264_PROFILE_BASELINE', 66);
define('H264_PROFILE_MAIN', 77);
define('H264_PROFILE_EXTENDED', 88);
define('H264_PROFILE_HIGH', 100);
define('H264_PROFILE_HIGH10', 110);
define('H264_PROFILE_HIGH422', 122);
define('H264_PROFILE_HIGH444', 144);
define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
class getid3_flv
{ {
const TAG_AUDIO = 8; function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
const TAG_VIDEO = 9; //$start_time = microtime(true);
const TAG_META = 18; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
const VIDEO_H263 = 2; $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
const VIDEO_SCREEN = 3; $FLVheader = fread($fd, 5);
const VIDEO_VP6 = 4;
$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));
public function Analyze() if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
{ $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
$info = &$this->getid3->info; unset($ThisFileInfo['flv']);
unset($ThisFileInfo['fileformat']);
$info['flv'] = array (); return false;
$info_flv = &$info['flv'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
$flv_data_length = $info['avdataend'] - $info['avdataoffset'];
$flv_header = fread($this->getid3->fp, 5);
$info['fileformat'] = 'flv';
$info_flv['header']['signature'] = substr($flv_header, 0, 3);
$info_flv['header']['version'] = getid3_lib::BigEndian2Int(substr($flv_header, 3, 1));
$type_flags = getid3_lib::BigEndian2Int(substr($flv_header, 4, 1));
$info_flv['header']['hasAudio'] = (bool) ($type_flags & 0x04);
$info_flv['header']['hasVideo'] = (bool) ($type_flags & 0x01);
$frame_size_data_length = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4));
$flv_header_frame_length = 9;
if ($frame_size_data_length > $flv_header_frame_length) {
fseek($this->getid3->fp, $frame_size_data_length - $flv_header_frame_length, SEEK_CUR);
} }
$duration = 0; $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
while ((ftell($this->getid3->fp) + 1) < $info['avdataend']) { $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
$this_tag_header = fread($this->getid3->fp, 16); $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>';
$previous_tag_length = getid3_lib::BigEndian2Int(substr($this_tag_header, 0, 4)); $Duration = 0;
$tag_type = getid3_lib::BigEndian2Int(substr($this_tag_header, 4, 1)); $found_video = false;
$data_length = getid3_lib::BigEndian2Int(substr($this_tag_header, 5, 3)); $found_audio = false;
$timestamp = getid3_lib::BigEndian2Int(substr($this_tag_header, 8, 3)); $found_meta = false;
$last_header_byte = getid3_lib::BigEndian2Int(substr($this_tag_header, 15, 1)); $tagParsed = 0;
$next_offset = ftell($this->getid3->fp) - 1 + $data_length; while (((ftell($fd) + 16) < $ThisFileInfo['avdataend']) && ($tagParsed <= 20 || !$found_meta)) {
$ThisTagHeader = fread($fd, 16);
switch ($tag_type) { $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;
}
case getid3_flv::TAG_AUDIO: //echo __LINE__.'['.ftell($fd).']=('.$TagType.')='.number_format(microtime(true) - $start_time, 3).'<br>';
if (!isset($info_flv['audio']['audioFormat'])) {
$info_flv['audio']['audioFormat'] = $last_header_byte & 0x07; switch ($TagType) {
$info_flv['audio']['audioRate'] = ($last_header_byte & 0x30) / 0x10; case GETID3_FLV_TAG_AUDIO:
$info_flv['audio']['audioSampleSize'] = ($last_header_byte & 0x40) / 0x40; if (!$found_audio) {
$info_flv['audio']['audioType'] = ($last_header_byte & 0x80) / 0x80; $found_audio = true;
$ThisFileInfo['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F;
$ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03;
$ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
$ThisFileInfo['flv']['audio']['audioType'] = $LastHeaderByte & 0x01;
} }
break; break;
case GETID3_FLV_TAG_VIDEO:
if (!$found_video) {
$found_video = true;
$ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
case getid3_flv::TAG_VIDEO: $FLVvideoHeader = fread($fd, 11);
if (!isset($info_flv['video']['videoCodec'])) {
$info_flv['video']['videoCodec'] = $last_header_byte & 0x07;
$flv_video_header = fread($this->getid3->fp, 11); if ($ThisFileInfo['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
// this code block contributed by: moysevichØgmail*com
if ($info_flv['video']['videoCodec'] != getid3_flv::VIDEO_VP6) { $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
// read AVCDecoderConfigurationRecord
$configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
$AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
$profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
$lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
$numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
$picture_size_type = (getid3_lib::BigEndian2Int(substr($flv_video_header, 3, 2))) >> 7; if (($numOfSequenceParameterSets & 0x1F) != 0) {
$picture_size_type = $picture_size_type & 0x0007; // there is at least one SequenceParameterSet
$info_flv['header']['videoSizeType'] = $picture_size_type; // read size of the first SequenceParameterSet
//$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
$spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
// read the first SequenceParameterSet
$sps = fread($fd, $spsSize);
if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
$spsReader = new AVCSequenceParameterSetReader($sps);
$spsReader->readData();
$ThisFileInfo['video']['resolution_x'] = $spsReader->getWidth();
$ThisFileInfo['video']['resolution_y'] = $spsReader->getHeight();
}
}
}
// end: moysevichØgmail*com
switch ($picture_size_type) { } elseif ($ThisFileInfo['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
$PictureSizeType = $PictureSizeType & 0x0007;
$ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
switch ($PictureSizeType) {
case 0: case 0:
$picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 5, 2)); //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
$picture_size_enc <<= 1; //$PictureSizeEnc <<= 1;
$info['video']['resolution_x'] = ($picture_size_enc & 0xFF00) >> 8; //$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
$picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 6, 2)); //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
$picture_size_enc <<= 1; //$PictureSizeEnc <<= 1;
$info['video']['resolution_y'] = ($picture_size_enc & 0xFF00) >> 8; //$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; break;
case 1: case 1:
$picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 5, 4)); $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
$picture_size_enc <<= 1; $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
$info['video']['resolution_x'] = ($picture_size_enc & 0xFFFF0000) >> 16; $PictureSizeEnc['x'] >>= 7;
$PictureSizeEnc['y'] >>= 7;
$picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 7, 4)); $ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
$picture_size_enc <<= 1; $ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
$info['video']['resolution_y'] = ($picture_size_enc & 0xFFFF0000) >> 16;
break; break;
case 2: case 2:
$info['video']['resolution_x'] = 352; $ThisFileInfo['video']['resolution_x'] = 352;
$info['video']['resolution_y'] = 288; $ThisFileInfo['video']['resolution_y'] = 288;
break; break;
case 3: case 3:
$info['video']['resolution_x'] = 176; $ThisFileInfo['video']['resolution_x'] = 176;
$info['video']['resolution_y'] = 144; $ThisFileInfo['video']['resolution_y'] = 144;
break; break;
case 4: case 4:
$info['video']['resolution_x'] = 128; $ThisFileInfo['video']['resolution_x'] = 128;
$info['video']['resolution_y'] = 96; $ThisFileInfo['video']['resolution_y'] = 96;
break; break;
case 5: case 5:
$info['video']['resolution_x'] = 320; $ThisFileInfo['video']['resolution_x'] = 320;
$info['video']['resolution_y'] = 240; $ThisFileInfo['video']['resolution_y'] = 240;
break; break;
case 6: case 6:
$info['video']['resolution_x'] = 160; $ThisFileInfo['video']['resolution_x'] = 160;
$info['video']['resolution_y'] = 120; $ThisFileInfo['video']['resolution_y'] = 120;
break; break;
default: default:
$info['video']['resolution_x'] = 0; $ThisFileInfo['video']['resolution_x'] = 0;
$info['video']['resolution_y'] = 0; $ThisFileInfo['video']['resolution_y'] = 0;
break; break;
} }
} }
$ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['video']['resolution_x'] / $ThisFileInfo['video']['resolution_y'];
} }
break; break;
// Meta tag // Meta tag
case getid3_flv::TAG_META: case GETID3_FLV_TAG_META:
if (!$found_meta) {
$found_meta = true;
fseek($fd, -1, SEEK_CUR);
$datachunk = fread($fd, $DataLength);
$AMFstream = new AMFStream($datachunk);
$reader = new AMFReader($AMFstream);
$eventName = $reader->readData();
$ThisFileInfo['flv']['meta'][$eventName] = $reader->readData();
unset($reader);
fseek($this->getid3->fp, -1, SEEK_CUR); $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate');
$reader = new AMFReader(new AMFStream(fread($this->getid3->fp, $data_length))); foreach ($copykeys as $sourcekey => $destkey) {
$event_name = $reader->readData(); if (isset($ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey])) {
$info['meta'][$event_name] = $reader->readData(); switch ($sourcekey) {
unset($reader); case 'width':
case 'height':
$info['video']['frame_rate'] = @$info['meta']['onMetaData']['framerate']; $ThisFileInfo['video'][$destkey] = intval(round($ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey]));
$info['video']['resolution_x'] = @$info['meta']['onMetaData']['width']; break;
$info['video']['resolution_y'] = @$info['meta']['onMetaData']['height']; case 'audiodatarate':
$ThisFileInfo['audio'][$destkey] = $ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey];
break;
case 'videodatarate':
case 'frame_rate':
default:
$ThisFileInfo['video'][$destkey] = $ThisFileInfo['flv']['meta']['onMetaData'][$sourcekey];
break;
}
}
}
}
break; break;
default: default:
@ -190,190 +268,175 @@ class getid3_flv extends getid3_handler
break; break;
} }
if ($timestamp > $duration) { fseek($fd, $NextOffset, SEEK_SET);
$duration = $timestamp;
}
fseek($this->getid3->fp, $next_offset, SEEK_SET); // Increase parsed tag count: break out of loop if more than 20 tags parsed
$tagParsed++;
} }
if ($info['playtime_seconds'] = $duration / 1000) { if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
$info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']; $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
} }
if ($info_flv['header']['hasAudio']) { if ($ThisFileInfo['flv']['header']['hasAudio']) {
$info['audio']['codec'] = $this->FLVaudioFormat($info_flv['audio']['audioFormat']); $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
$info['audio']['sample_rate'] = $this->FLVaudioRate($info_flv['audio']['audioRate']); $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
$info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info_flv['audio']['audioSampleSize']); $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
$info['audio']['channels'] = $info_flv['audio']['audioType'] + 1; // 0=mono,1=stereo $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
$info['audio']['lossless'] = ($info_flv['audio']['audioFormat'] ? false : true); // 0=uncompressed $ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
$info['audio']['dataformat'] = 'flv'; $ThisFileInfo['audio']['dataformat'] = 'flv';
} }
if (@$info_flv['header']['hasVideo']) { if (@$ThisFileInfo['flv']['header']['hasVideo']) {
$info['video']['codec'] = $this->FLVvideoCodec($info_flv['video']['videoCodec']); $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
$info['video']['dataformat'] = 'flv'; $ThisFileInfo['video']['dataformat'] = 'flv';
$info['video']['lossless'] = false; $ThisFileInfo['video']['lossless'] = false;
} }
// Set information from meta
if (isset($ThisFileInfo['flv']['meta']['onMetaData']['duration'])) {
$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flv']['meta']['onMetaData']['duration'];
}
if (isset($ThisFileInfo['flv']['meta']['onMetaData']['audiocodecid'])) {
$ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['meta']['onMetaData']['audiocodecid']);
}
if (isset($ThisFileInfo['flv']['meta']['onMetaData']['videocodecid'])) {
$ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['meta']['onMetaData']['videocodecid']);
}
return true; return true;
} }
public static function FLVaudioFormat($id) { function FLVaudioFormat($id) {
$FLVaudioFormat = array(
static $lookup = array( 0 => 'Linear PCM, platform endian',
0 => 'uncompressed', 1 => 'ADPCM',
1 => 'ADPCM', 2 => 'mp3',
2 => 'mp3', 3 => 'Linear PCM, little endian',
5 => 'Nellymoser 8kHz mono', 4 => 'Nellymoser 16kHz mono',
6 => 'Nellymoser', 5 => 'Nellymoser 8kHz mono',
6 => 'Nellymoser',
7 => 'G.711A-law logarithmic PCM',
8 => 'G.711 mu-law logarithmic PCM',
9 => 'reserved',
10 => 'AAC',
11 => false, // unknown?
12 => false, // unknown?
13 => false, // unknown?
14 => 'mp3 8kHz',
15 => 'Device-specific sound',
); );
return (@$lookup[$id] ? @$lookup[$id] : false); return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
} }
function FLVaudioRate($id) {
public static function FLVaudioRate($id) { $FLVaudioRate = array(
static $lookup = array(
0 => 5500, 0 => 5500,
1 => 11025, 1 => 11025,
2 => 22050, 2 => 22050,
3 => 44100, 3 => 44100,
); );
return (@$lookup[$id] ? @$lookup[$id] : false); return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
} }
function FLVaudioBitDepth($id) {
public static function FLVaudioBitDepth($id) { $FLVaudioBitDepth = array(
static $lookup = array(
0 => 8, 0 => 8,
1 => 16, 1 => 16,
); );
return (@$lookup[$id] ? @$lookup[$id] : false); return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
} }
function FLVvideoCodec($id) {
public static function FLVvideoCodec($id) { $FLVvideoCodec = array(
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
static $lookup = array( GETID3_FLV_VIDEO_SCREEN => 'Screen video',
getid3_flv::VIDEO_H263 => 'Sorenson H.263', GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
getid3_flv::VIDEO_SCREEN => 'Screen video', GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
getid3_flv::VIDEO_VP6 => 'On2 VP6', GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
); );
return (@$lookup[$id] ? @$lookup[$id] : false); return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
} }
} }
class AMFStream {
var $bytes;
var $pos;
function AMFStream(&$bytes) {
class AMFStream $this->bytes =& $bytes;
{
public $bytes;
public $pos;
public function AMFStream($bytes) {
$this->bytes = $bytes;
$this->pos = 0; $this->pos = 0;
} }
function readByte() {
public function readByte() {
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
} }
function readInt() {
public function readInt() {
return ($this->readByte() << 8) + $this->readByte(); return ($this->readByte() << 8) + $this->readByte();
} }
function readLong() {
public function readLong() {
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
} }
function readDouble() {
public function readDouble() {
return getid3_lib::BigEndian2Float($this->read(8)); return getid3_lib::BigEndian2Float($this->read(8));
} }
function readUTF() {
public function readUTF() {
$length = $this->readInt(); $length = $this->readInt();
return $this->read($length); return $this->read($length);
} }
function readLongUTF() {
public function readLongUTF() {
$length = $this->readLong(); $length = $this->readLong();
return $this->read($length); return $this->read($length);
} }
function read($length) {
public function read($length) {
$val = substr($this->bytes, $this->pos, $length); $val = substr($this->bytes, $this->pos, $length);
$this->pos += $length; $this->pos += $length;
return $val; return $val;
} }
function peekByte() {
public function peekByte() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readByte(); $val = $this->readByte();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekInt() {
public function peekInt() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readInt(); $val = $this->readInt();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekLong() {
public function peekLong() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readLong(); $val = $this->readLong();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekDouble() {
public function peekDouble() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readDouble(); $val = $this->readDouble();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekUTF() {
public function peekUTF() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readUTF(); $val = $this->readUTF();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekLongUTF() {
public function peekLongUTF() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readLongUTF(); $val = $this->readLongUTF();
$this->pos = $pos; $this->pos = $pos;
@ -381,25 +444,19 @@ class AMFStream
} }
} }
class AMFReader {
var $stream;
function AMFReader(&$stream) {
class AMFReader $this->stream =& $stream;
{
public $stream;
public function __construct($stream) {
$this->stream = $stream;
} }
function readData() {
public function readData() {
$value = null; $value = null;
$type = $this->stream->readByte(); $type = $this->stream->readByte();
switch ($type) {
switch($type) {
// Double // Double
case 0: case 0:
$value = $this->readDouble(); $value = $this->readDouble();
@ -464,112 +521,196 @@ class AMFReader
return $value; return $value;
} }
function readDouble() {
public function readDouble() {
return $this->stream->readDouble(); return $this->stream->readDouble();
} }
function readBoolean() {
public function readBoolean() {
return $this->stream->readByte() == 1; return $this->stream->readByte() == 1;
} }
function readString() {
public function readString() {
return $this->stream->readUTF(); return $this->stream->readUTF();
} }
function readObject() {
public function readObject() {
// Get highest numerical index - ignored // Get highest numerical index - ignored
$highestIndex = $this->stream->readLong(); // $highestIndex = $this->stream->readLong();
$data = array(); $data = array();
while ($key = $this->stream->readUTF()) { while ($key = $this->stream->readUTF()) {
// Mixed array record ends with empty string (0x00 0x00) and 0x09
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
// Consume byte
$this->stream->readByte();
break;
}
$data[$key] = $this->readData(); $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; return $data;
} }
function readMixedArray() {
public function readMixedArray() {
// Get highest numerical index - ignored // Get highest numerical index - ignored
$highestIndex = $this->stream->readLong(); $highestIndex = $this->stream->readLong();
$data = array(); $data = array();
while ($key = $this->stream->readUTF()) { while ($key = $this->stream->readUTF()) {
// Mixed array record ends with empty string (0x00 0x00) and 0x09
if (($key == '') && ($this->stream->peekByte() == 0x09)) {
// Consume byte
$this->stream->readByte();
break;
}
if (is_numeric($key)) { if (is_numeric($key)) {
$key = (float) $key; $key = (float) $key;
} }
$data[$key] = $this->readData(); $data[$key] = $this->readData();
} }
// Mixed array record ends with empty string (0x00 0x00) and 0x09
return $data; if (($key == '') && ($this->stream->peekByte() == 0x09)) {
} // Consume byte
$this->stream->readByte();
public function readArray() {
$length = $this->stream->readLong();
$data = array();
for ($i = 0; $i < count($length); $i++) {
$data[] = $this->readData();
} }
return $data; return $data;
} }
function readArray() {
$length = $this->stream->readLong();
$data = array();
public function readDate() { for ($i = 0; $i < $length; $i++) {
$data[] = $this->readData();
}
return $data;
}
function readDate() {
$timestamp = $this->stream->readDouble(); $timestamp = $this->stream->readDouble();
$timezone = $this->stream->readInt(); $timezone = $this->stream->readInt();
return $timestamp; return $timestamp;
} }
function readLongString() {
public function readLongString() {
return $this->stream->readLongUTF(); return $this->stream->readLongUTF();
} }
function readXML() {
public function readXML() {
return $this->stream->readLongUTF(); return $this->stream->readLongUTF();
} }
function readTypedObject() {
public function readTypedObject() {
$className = $this->stream->readUTF(); $className = $this->stream->readUTF();
return $this->readObject(); return $this->readObject();
} }
} }
class AVCSequenceParameterSetReader {
var $sps;
var $start = 0;
var $currentBytes = 0;
var $currentBits = 0;
var $width;
var $height;
function AVCSequenceParameterSetReader($sps) {
$this->sps = $sps;
}
function readData() {
$this->skipBits(8);
$this->skipBits(8);
$profile = $this->getBits(8); // read profile
$this->skipBits(16);
$this->expGolombUe(); // read sps id
if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) {
if ($this->expGolombUe() == 3) {
$this->skipBits(1);
}
$this->expGolombUe();
$this->expGolombUe();
$this->skipBits(1);
if ($this->getBit()) {
for ($i = 0; $i < 8; $i++) {
if ($this->getBit()) {
$size = $i < 6 ? 16 : 64;
$lastScale = 8;
$nextScale = 8;
for ($j = 0; $j < $size; $j++) {
if ($nextScale != 0) {
$deltaScale = $this->expGolombUe();
$nextScale = ($lastScale + $deltaScale + 256) % 256;
}
if ($nextScale != 0) {
$lastScale = $nextScale;
}
}
}
}
}
}
$this->expGolombUe();
$pocType = $this->expGolombUe();
if ($pocType == 0) {
$this->expGolombUe();
} elseif ($pocType == 1) {
$this->skipBits(1);
$this->expGolombSe();
$this->expGolombSe();
$pocCycleLength = $this->expGolombUe();
for ($i = 0; $i < $pocCycleLength; $i++) {
$this->expGolombSe();
}
}
$this->expGolombUe();
$this->skipBits(1);
$this->width = ($this->expGolombUe() + 1) * 16;
$heightMap = $this->expGolombUe() + 1;
$this->height = (2 - $this->getBit()) * $heightMap * 16;
}
function skipBits($bits) {
$newBits = $this->currentBits + $bits;
$this->currentBytes += (int)floor($newBits / 8);
$this->currentBits = $newBits % 8;
}
function getBit() {
$result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
$this->skipBits(1);
return $result;
}
function getBits($bits) {
$result = 0;
for ($i = 0; $i < $bits; $i++) {
$result = ($result << 1) + $this->getBit();
}
return $result;
}
function expGolombUe() {
$significantBits = 0;
$bit = $this->getBit();
while ($bit == 0) {
$significantBits++;
$bit = $this->getBit();
}
return (1 << $significantBits) + $this->getBits($significantBits) - 1;
}
function expGolombSe() {
$result = $this->expGolombUe();
if (($result & 0x01) == 0) {
return -($result >> 1);
} else {
return ($result + 1) >> 1;
}
}
function getWidth() {
return $this->width;
}
function getHeight() {
return $this->height;
}
}
?> ?>

File diff suppressed because it is too large Load diff

View file

@ -1,323 +1,290 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio-video.mpeg.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing MPEG files //
// +----------------------------------------------------------------------+ // dependencies: module.audio.mp3.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
// +----------------------------------------------------------------------+
// | module.audio-video.mpeg.php | define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
// | Module for analyzing MPEG files | define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
// | dependencies: module.audio.mp3.php | 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");
// $Id: module.audio-video.mpeg.php,v 1.3 2006/11/02 10:48:00 ah Exp $ 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
class getid3_mpeg extends getid3_handler
{ {
const VIDEO_PICTURE_START = "\x00\x00\x01\x00"; function getid3_mpeg(&$fd, &$ThisFileInfo) {
const VIDEO_USER_DATA_START = "\x00\x00\x01\xB2"; if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) {
const VIDEO_SEQUENCE_HEADER = "\x00\x00\x01\xB3"; $ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')';
const VIDEO_SEQUENCE_ERROR = "\x00\x00\x01\xB4"; return false;
const VIDEO_EXTENSION_START = "\x00\x00\x01\xB5"; }
const VIDEO_SEQUENCE_END = "\x00\x00\x01\xB7"; $ThisFileInfo['fileformat'] = 'mpeg';
const VIDEO_GROUP_START = "\x00\x00\x01\xB8"; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
const AUDIO_START = "\x00\x00\x01\xC0"; $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']));
$MPEGstreamDataLength = strlen($MPEGstreamData);
public function Analyze() { $foundVideo = true;
$VideoChunkOffset = 0;
$getid3 = $this->getid3; while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
if ($VideoChunkOffset >= $MPEGstreamDataLength) {
$getid3->info['mpeg']['video']['raw'] = array (); $foundVideo = false;
$info_mpeg_video = &$getid3->info['mpeg']['video']; break;
$info_mpeg_video_raw = &$info_mpeg_video['raw']; }
}
$getid3->info['video'] = array (); if ($foundVideo) {
$info_video = &$getid3->info['video'];
// Start code 32 bits
$getid3->include_module('audio.mp3'); // horizontal frame size 12 bits
// vertical frame size 12 bits
if ($getid3->info['avdataend'] <= $getid3->info['avdataoffset']) { // pixel aspect ratio 4 bits
throw new getid3_exception('"avdataend" ('.$getid3->info['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$getid3->info['avdataoffset'].')'); // frame rate 4 bits
} // bitrate 18 bits
// marker bit 1 bit
$getid3->info['fileformat'] = 'mpeg'; // VBV buffer size 10 bits
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); // constrained parameter flag 1 bit
$mpeg_stream_data = fread($getid3->fp, min(100000, $getid3->info['avdataend'] - $getid3->info['avdataoffset'])); // intra quant. matrix flag 1 bit
$mpeg_stream_data_length = strlen($mpeg_stream_data); // intra quant. matrix values 512 bits (present if matrix flag == 1)
// non-intra quant. matrix flag 1 bit
$video_chunk_offset = 0; // non-intra quant. matrix values 512 bits (present if matrix flag == 1)
while (substr($mpeg_stream_data, $video_chunk_offset++, 4) !== getid3_mpeg::VIDEO_SEQUENCE_HEADER) {
if ($video_chunk_offset >= $mpeg_stream_data_length) { $ThisFileInfo['video']['dataformat'] = 'mpeg';
throw new getid3_exception('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?');
} $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
}
$FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
// Start code 32 bits $VideoChunkOffset += 3;
// horizontal frame size 12 bits
// vertical frame size 12 bits $AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
// pixel aspect ratio 4 bits $VideoChunkOffset += 1;
// frame rate 4 bits
// bitrate 18 bits $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
// marker bit 1 bit $VideoChunkOffset += 4;
// VBV buffer size 10 bits
// constrained parameter flag 1 bit $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
// intra quant. matrix flag 1 bit $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size
// intra quant. matrix values 512 bits (present if matrix flag == 1) $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
// non-intra quant. matrix flag 1 bit $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F);
// non-intra quant. matrix values 512 bits (present if matrix flag == 1)
$ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'];
$info_video['dataformat'] = 'mpeg'; $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'];
$video_chunk_offset += (strlen(getid3_mpeg::VIDEO_SEQUENCE_HEADER) - 1); $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']);
$frame_size_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 3)); $ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']);
$video_chunk_offset += 3;
$ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18));
$aspect_ratio_frame_rate_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 1)); $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1));
$video_chunk_offset += 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));
$assorted_information = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 4)); $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1));
$video_chunk_offset += 4; if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) {
$info_mpeg_video_raw['framesize_horizontal'] = ($frame_size_dword & 0xFFF000) >> 12; // 12 bits for horizontal frame size // read 512 bits
$info_mpeg_video_raw['framesize_vertical'] = ($frame_size_dword & 0x000FFF); // 12 bits for vertical frame size $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
$info_mpeg_video_raw['pixel_aspect_ratio'] = ($aspect_ratio_frame_rate_dword & 0xF0) >> 4; $VideoChunkOffset += 64;
$info_mpeg_video_raw['frame_rate'] = ($aspect_ratio_frame_rate_dword & 0x0F);
$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1));
$info_mpeg_video['framesize_horizontal'] = $info_mpeg_video_raw['framesize_horizontal']; $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
$info_mpeg_video['framesize_vertical'] = $info_mpeg_video_raw['framesize_vertical'];
if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
$info_mpeg_video['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info_mpeg_video_raw['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
$info_mpeg_video['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info_mpeg_video_raw['pixel_aspect_ratio']); $VideoChunkOffset += 64;
$info_mpeg_video['frame_rate'] = $this->MPEGvideoFramerateLookup($info_mpeg_video_raw['frame_rate']); }
$info_mpeg_video_raw['bitrate'] = bindec(substr($assorted_information, 0, 18)); } else {
$info_mpeg_video_raw['marker_bit'] = (bool)bindec($assorted_information{18});
$info_mpeg_video_raw['vbv_buffer_size'] = bindec(substr($assorted_information, 19, 10)); $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1));
$info_mpeg_video_raw['constrained_param_flag'] = (bool)bindec($assorted_information{29}); if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
$info_mpeg_video_raw['intra_quant_flag'] = (bool)bindec($assorted_information{30}); $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
$VideoChunkOffset += 64;
if ($info_mpeg_video_raw['intra_quant_flag']) { }
// read 512 bits }
$info_mpeg_video_raw['intra_quant'] = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64));
$video_chunk_offset += 64; if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
$info_mpeg_video_raw['non_intra_quant_flag'] = (bool)bindec($info_mpeg_video_raw['intra_quant']{511}); $ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files';
$info_mpeg_video_raw['intra_quant'] = bindec($assorted_information{31}).substr(getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64)), 0, 511); $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr';
if ($info_mpeg_video_raw['non_intra_quant_flag']) { } else {
$info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64);
$video_chunk_offset += 64; $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400;
} $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr';
$ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate'];
} else {
}
$info_mpeg_video_raw['non_intra_quant_flag'] = (bool)bindec($assorted_information{31});
if ($info_mpeg_video_raw['non_intra_quant_flag']) { $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal'];
$info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64); $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical'];
$video_chunk_offset += 64; $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;
if ($info_mpeg_video_raw['bitrate'] == 0x3FFFF) { // 18 set bits $ThisFileInfo['video']['bits_per_sample'] = 24;
$getid3->warning('This version of getID3() cannot determine average bitrate of VBR MPEG video files'); } else {
$info_mpeg_video['bitrate_mode'] = 'vbr';
$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?';
} else {
}
$info_mpeg_video['bitrate'] = $info_mpeg_video_raw['bitrate'] * 400;
$info_mpeg_video['bitrate_mode'] = 'cbr'; //0x000001B3 begins the sequence_header of every MPEG video stream.
$info_video['bitrate'] = $info_mpeg_video['bitrate']; //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.)
$info_video['resolution_x'] = $info_mpeg_video['framesize_horizontal']; //MPEG-1 doesn't have this extension, so that's a sure way to tell the
$info_video['resolution_y'] = $info_mpeg_video['framesize_vertical']; //difference between MPEG-1 and MPEG-2 video streams.
$info_video['frame_rate'] = $info_mpeg_video['frame_rate'];
$info_video['bitrate_mode'] = $info_mpeg_video['bitrate_mode']; if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
$info_video['pixel_aspect_ratio'] = $info_mpeg_video['pixel_aspect_ratio']; $ThisFileInfo['video']['codec'] = 'MPEG-2';
$info_video['lossless'] = false; } else {
$info_video['bits_per_sample'] = 24; $ThisFileInfo['video']['codec'] = 'MPEG-1';
}
//0x000001B3 begins the sequence_header of every MPEG video stream.
//But in MPEG-2, this header must immediately be followed by an $AudioChunkOffset = 0;
//extension_start_code (0x000001B5) with a sequence_extension ID (1). while (true) {
//(This extension contains all the additional MPEG-2 stuff.) while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
//MPEG-1 doesn't have this extension, so that's a sure way to tell the if ($AudioChunkOffset >= $MPEGstreamDataLength) {
//difference between MPEG-1 and MPEG-2 video streams. break 2;
}
$info_video['codec'] = substr($mpeg_stream_data, $video_chunk_offset, 4) == getid3_mpeg::VIDEO_EXTENSION_START ? 'MPEG-2' : 'MPEG-1'; }
$audio_chunk_offset = 0; for ($i = 0; $i <= 7; $i++) {
while (true) { // 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
while (substr($mpeg_stream_data, $audio_chunk_offset++, 4) !== getid3_mpeg::AUDIO_START) { // I have no idea why or what the difference is, so this is a stupid hack.
if ($audio_chunk_offset >= $mpeg_stream_data_length) { // If anybody has any better idea of what's going on, please let me know - info@getid3.org
break 2;
} $dummy = $ThisFileInfo;
} if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) {
$ThisFileInfo = $dummy;
for ($i = 0; $i <= 7; $i++) { $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
// 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 $ThisFileInfo['audio']['lossless'] = false;
// I have no idea why or what the difference is, so this is a stupid hack. break 2;
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
}
// make copy of info }
$dummy = $getid3->info; }
// clone getid3 - better safe than sorry // Temporary hack to account for interleaving overhead:
$clone = clone $this->getid3; if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) {
$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']);
// check
$mp3 = new getid3_mp3($clone); // Interleaved MPEG audio/video files have a certain amount of overhead that varies
if ($mp3->decodeMPEGaudioHeader($getid3->fp, ($audio_chunk_offset + 3) + 8 + $i, $dummy, false)) { // 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
$getid3->info = $dummy; // playtime is calculated as filesize / total-bitrate
$getid3->info['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']);
$getid3->info['audio']['lossless'] = false;
break 2; //switch ($ThisFileInfo['video']['bitrate']) {
} // case('5000000'):
// $multiplier = 0.93292642112380355828048824319889;
// destroy copy // break;
unset($dummy); // case('5500000'):
} // $multiplier = 0.93582895375200989965359777343219;
} // break;
// case('6000000'):
// Temporary hack to account for interleaving overhead: // $multiplier = 0.93796247714820932532911373859139;
if (!empty($info_video['bitrate']) && !empty($getid3->info['audio']['bitrate'])) { // break;
$getid3->info['playtime_seconds'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / ($info_video['bitrate'] + $getid3->info['audio']['bitrate']); // case('7000000'):
// $multiplier = 0.9413264083635103463010117778776;
// Interleaved MPEG audio/video files have a certain amount of overhead that varies // break;
// by both video and audio bitrates, and not in any sensible, linear/logarithmic patter // default:
// Use interpolated lookup tables to approximately guess how much is overhead, because // $multiplier = 1;
// playtime is calculated as filesize / total-bitrate // break;
$getid3->info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info_video['bitrate'], $getid3->info['audio']['bitrate']); //}
//$ThisFileInfo['playtime_seconds'] *= $multiplier;
//switch ($info_video['bitrate']) { //$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.';
// case('5000000'): if ($ThisFileInfo['video']['bitrate'] < 50000) {
// $multiplier = 0.93292642112380355828048824319889; $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.';
// break; }
// case('5500000'): }
// $multiplier = 0.93582895375200989965359777343219;
// break; return true;
// case('6000000'): }
// $multiplier = 0.93796247714820932532911373859139;
// break;
// case('7000000'): function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
// $multiplier = 0.9413264083635103463010117778776; $OverheadPercentage = 0;
// break;
// default: $AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
// $multiplier = 1; $VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
// break;
//}
//$getid3->info['playtime_seconds'] *= $multiplier; //OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
//$getid3->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.'); $OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
if ($info_video['bitrate'] < 50000) { $OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
$getid3->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.'); $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);
return true; $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);
public static function MPEGsystemNonOverheadPercentage($video_bitrate, $audio_bitrate) {
$BitrateToUseMin = 32;
$overhead_percentage = 0; $BitrateToUseMax = 32;
$previousBitrate = 32;
$audio_bitrate = max(min($audio_bitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?) foreach ($OverheadMultiplierByBitrate as $key => $value) {
$video_bitrate = max(min($video_bitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss) if ($AudioBitrate >= $previousBitrate) {
$BitrateToUseMin = $previousBitrate;
//OMBB[audiobitrate] = array ( video-10kbps, video-100kbps, video-1000kbps, video-10000kbps) }
static $overhead_multiplier_by_bitrate = array ( if ($AudioBitrate < $key) {
32 => array (0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940), $BitrateToUseMax = $key;
48 => array (0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960), break;
56 => array (0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340), }
64 => array (0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470), $previousBitrate = $key;
96 => array (0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690), }
128 => array (0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050), $FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
160 => array (0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570),
192 => array (0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620), $VideoBitrateLog10 = log10($VideoBitrate);
224 => array (0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480), $VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
256 => array (0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790), $VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
320 => array (0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190), $VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
384 => array (0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890) $VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
); $FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
$bitrate_to_use_min = $bitrate_to_use_max = $previous_bitrate = 32; $OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
foreach ($overhead_multiplier_by_bitrate as $key => $value) { $OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
if ($audio_bitrate >= $previous_bitrate) {
$bitrate_to_use_min = $previous_bitrate; return $OverheadPercentage;
} }
if ($audio_bitrate < $key) {
$bitrate_to_use_max = $key;
break; function MPEGvideoFramerateLookup($rawframerate) {
} $MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
$previous_bitrate = $key; return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
} }
$factor_a = ($bitrate_to_use_max - $audio_bitrate) / ($bitrate_to_use_max - $bitrate_to_use_min); 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);
$video_bitrate_log10 = log10($video_bitrate); return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
$video_factor_min1 = $overhead_multiplier_by_bitrate[$bitrate_to_use_min][floor($video_bitrate_log10)]; }
$video_factor_min2 = $overhead_multiplier_by_bitrate[$bitrate_to_use_max][floor($video_bitrate_log10)];
$video_factor_max1 = $overhead_multiplier_by_bitrate[$bitrate_to_use_min][ceil($video_bitrate_log10)]; function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
$video_factor_max2 = $overhead_multiplier_by_bitrate[$bitrate_to_use_max][ceil($video_bitrate_log10)]; $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');
$factor_v = $video_bitrate_log10 - floor($video_bitrate_log10); return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
}
$overhead_percentage = $video_factor_min1 * $factor_a * $factor_v;
$overhead_percentage += $video_factor_min2 * (1 - $factor_a) * $factor_v;
$overhead_percentage += $video_factor_max1 * $factor_a * (1 - $factor_v);
$overhead_percentage += $video_factor_max2 * (1 - $factor_a) * (1 - $factor_v);
return $overhead_percentage;
}
public static function MPEGvideoFramerateLookup($raw_frame_rate) {
$lookup = array (0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
return (float)(isset($lookup[$raw_frame_rate]) ? $lookup[$raw_frame_rate] : 0);
}
public static function MPEGvideoAspectRatioLookup($raw_aspect_ratio) {
$lookup = array (0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
return (float)(isset($lookup[$raw_aspect_ratio]) ? $lookup[$raw_aspect_ratio] : 0);
}
public static function MPEGvideoAspectRatioTextLookup($raw_aspect_ratio) {
$lookup = array ('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
return (isset($lookup[$raw_aspect_ratio]) ? $lookup[$raw_aspect_ratio] : '');
}
} }

View file

@ -1,209 +1,222 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.nsv.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Nullsoft NSV files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio-video.nsv.php |
// | module for analyzing Nullsoft NSV files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio-video.nsv.php,v 1.3 2006/11/02 10:48:00 ah Exp $
class getid3_nsv
class getid3_nsv extends getid3_handler
{ {
public function Analyze() { function getid3_nsv(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$NSVheader = fread($fd, 4);
$getid3->info['fileformat'] = 'nsv'; switch ($NSVheader) {
$getid3->info['audio']['dataformat'] = 'nsv'; case 'NSVs':
$getid3->info['video']['dataformat'] = 'nsv'; if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) {
$getid3->info['audio']['lossless'] = false; $ThisFileInfo['fileformat'] = 'nsv';
$getid3->info['video']['lossless'] = false; $ThisFileInfo['audio']['dataformat'] = 'nsv';
$ThisFileInfo['video']['dataformat'] = 'nsv';
$ThisFileInfo['audio']['lossless'] = false;
$ThisFileInfo['video']['lossless'] = false;
}
break;
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); case 'NSVf':
$nsv_header = fread($getid3->fp, 4); 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;
switch ($nsv_header) { default:
$ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"';
return false;
break;
}
case 'NSVs': if (!isset($ThisFileInfo['nsv']['NSVf'])) {
$this->getNSVsHeader(); $ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
break; }
case 'NSVf': return true;
if ($this->getNSVfHeader()) { }
$this->getNSVsHeader($getid3->info['nsv']['NSVf']['header_length']);
}
break;
default: function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) {
throw new getid3_exception('Expecting "NSVs" or "NSVf" at offset '.$getid3->info['avdataoffset'].', found "'.$nsv_header.'"'); fseek($fd, $fileoffset, SEEK_SET);
break; $NSVsheader = fread($fd, 28);
} $offset = 0;
if (!isset($getid3->info['nsv']['NSVf'])) { $ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
$getid3->warning('NSVf header not present - cannot calculate playtime or bitrate'); $offset += 4;
}
return true; 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;
}
private function getNSVsHeader($file_offset = 0) { static $NSVframerateLookup = array();
if (empty($NSVframerateLookup)) {
$getid3 = $this->getid3; $NSVframerateLookup[129] = (float) 29.970;
$NSVframerateLookup[131] = (float) 23.976;
fseek($getid3->fp, $file_offset, SEEK_SET); $NSVframerateLookup[133] = (float) 14.985;
$nsvs_header = fread($getid3->fp, 28); $NSVframerateLookup[197] = (float) 59.940;
$NSVframerateLookup[199] = (float) 47.952;
$getid3->info['nsv']['NSVs'] = array (); }
$info_nsv_NSVs = &$getid3->info['nsv']['NSVs']; return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
}
$info_nsv_NSVs['identifier'] = substr($nsvs_header, 0, 4);
if ($info_nsv_NSVs['identifier'] != 'NSVs') {
throw new getid3_exception('expected "NSVs" at offset ('.$file_offset.'), found "'.$info_nsv_NSVs['identifier'].'" instead');
}
$info_nsv_NSVs['offset'] = $file_offset;
getid3_lib::ReadSequence('LittleEndian2Int', $info_nsv_NSVs, $nsvs_header, 4,
array (
'video_codec' => -4, // string
'audio_codec' => -4, // string
'resolution_x' => 2,
'resolution_y' => 2,
'framerate_index' => 1,
)
);
if ($info_nsv_NSVs['audio_codec'] == 'PCM ') {
getid3_lib::ReadSequence('LittleEndian2Int', $info_nsv_NSVs, $nsvs_header, 24,
array (
'bits_channel' => 1,
'channels' => 1,
'sample_rate' => 2
)
);
$getid3->info['audio']['sample_rate'] = $info_nsv_NSVs['sample_rate'];
}
$getid3->info['video']['resolution_x'] = $info_nsv_NSVs['resolution_x'];
$getid3->info['video']['resolution_y'] = $info_nsv_NSVs['resolution_y'];
$info_nsv_NSVs['frame_rate'] = getid3_nsv::NSVframerateLookup($info_nsv_NSVs['framerate_index']);
$getid3->info['video']['frame_rate'] = $info_nsv_NSVs['frame_rate'];
$getid3->info['video']['bits_per_sample'] = 24;
$getid3->info['video']['pixel_aspect_ratio'] = (float)1;
return true;
}
private function getNSVfHeader($file_offset = 0, $get_toc_offsets=false) {
$getid3 = $this->getid3;
fseek($getid3->fp, $file_offset, SEEK_SET);
$nsvf_header = fread($getid3->fp, 28);
$getid3->info['nsv']['NSVf'] = array ();
$info_nsv_NSVf = &$getid3->info['nsv']['NSVf'];
$info_nsv_NSVf['identifier'] = substr($nsvf_header, 0, 4);
if ($info_nsv_NSVf['identifier'] != 'NSVf') {
throw new getid3_exception('expected "NSVf" at offset ('.$file_offset.'), found "'.$info_nsv_NSVf['identifier'].'" instead');
}
$getid3->info['nsv']['NSVs']['offset'] = $file_offset;
getid3_lib::ReadSequence('LittleEndian2Int', $info_nsv_NSVf, $nsvf_header, 4,
array (
'header_length' => 4,
'file_size' => 4,
'playtime_ms' => 4,
'meta_size' => 4,
'TOC_entries_1' => 4,
'TOC_entries_2' => 4
)
);
if ($info_nsv_NSVf['playtime_ms'] == 0) {
throw new getid3_exception('Corrupt NSV file: NSVf.playtime_ms == zero');
}
if ($info_nsv_NSVf['file_size'] > $getid3->info['avdataend']) {
$getid3->warning('truncated file - NSVf header indicates '.$info_nsv_NSVf['file_size'].' bytes, file actually '.$getid3->info['avdataend'].' bytes');
}
$nsvf_header .= fread($getid3->fp, $info_nsv_NSVf['meta_size'] + (4 * $info_nsv_NSVf['TOC_entries_1']) + (4 * $info_nsv_NSVf['TOC_entries_2']));
$nsvf_headerlength = strlen($nsvf_header);
$info_nsv_NSVf['metadata'] = substr($nsvf_header, 28, $info_nsv_NSVf['meta_size']);
$offset = 28 + $info_nsv_NSVf['meta_size'];
if ($get_toc_offsets) {
$toc_counter = 0;
while ($toc_counter < $info_nsv_NSVf['TOC_entries_1']) {
if ($toc_counter < $info_nsv_NSVf['TOC_entries_1']) {
$info_nsv_NSVf['TOC_1'][$toc_counter] = getid3_lib::LittleEndian2Int(substr($nsvf_header, $offset, 4));
$offset += 4;
$toc_counter++;
}
}
}
if (trim($info_nsv_NSVf['metadata']) != '') {
$info_nsv_NSVf['metadata'] = str_replace('`', "\x01", $info_nsv_NSVf['metadata']);
$comment_pair_array = explode("\x01".' ', $info_nsv_NSVf['metadata']);
foreach ($comment_pair_array as $comment_pair) {
if (strstr($comment_pair, '='."\x01")) {
list($key, $value) = explode('='."\x01", $comment_pair, 2);
$getid3->info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
}
}
}
$getid3->info['playtime_seconds'] = $info_nsv_NSVf['playtime_ms'] / 1000;
$getid3->info['bitrate'] = ($info_nsv_NSVf['file_size'] * 8) / $getid3->info['playtime_seconds'];
return true;
}
public static function NSVframerateLookup($frame_rate_index) {
if ($frame_rate_index <= 127) {
return (float)$frame_rate_index;
}
static $lookup = array (
129 => 29.970,
131 => 23.976,
133 => 14.985,
197 => 59.940,
199 => 47.952
);
return (isset($lookup[$frame_rate_index]) ? $lookup[$frame_rate_index] : false);
}
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,153 +1,147 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio-video.swf.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Shockwave Flash files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio-video.swf.php |
// | module for analyzing Macromedia Shockwave Flash files. |
// | dependencies: zlib support in PHP |
// +----------------------------------------------------------------------+
//
// $Id: module.audio-video.swf.php,v 1.2 2006/11/02 10:48:00 ah Exp $
class getid3_swf
class getid3_swf extends getid3_handler
{ {
public function Analyze() { function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
//$start_time = microtime(true);
$ThisFileInfo['fileformat'] = 'swf';
$ThisFileInfo['video']['dataformat'] = 'swf';
$getid3 = $this->getid3; // http://www.openswf.org/spec/SWFfileformat.html
$getid3->info['fileformat'] = 'swf'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->info['video']['dataformat'] = 'swf';
// http://www.openswf.org/spec/SWFfileformat.html $SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
switch ($ThisFileInfo['swf']['header']['signature']) {
case 'FWS':
$ThisFileInfo['swf']['header']['compressed'] = false;
break;
$swf_file_data = fread($getid3->fp, $getid3->info['avdataend'] - $getid3->info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data case 'CWS':
$ThisFileInfo['swf']['header']['compressed'] = true;
break;
$getid3->info['swf']['header']['signature'] = substr($swf_file_data, 0, 3); default:
switch ($getid3->info['swf']['header']['signature']) { $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));
case 'FWS': //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
$getid3->info['swf']['header']['compressed'] = false;
break;
case 'CWS': if ($ThisFileInfo['swf']['header']['compressed']) {
$getid3->info['swf']['header']['compressed'] = true;
break;
default: $SWFHead = substr($SWFfileData, 0, 8);
throw new getid3_exception('Expecting "FWS" or "CWS" at offset '.$getid3->info['avdataoffset'].', found "'.$getid3->info['swf']['header']['signature'].'"'); $SWFfileData = substr($SWFfileData, 8);
} if ($decompressed = @gzuncompress($SWFfileData)) {
$getid3->info['swf']['header']['version'] = getid3_lib::LittleEndian2Int($swf_file_data{3});
$getid3->info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($swf_file_data, 4, 4));
if (!function_exists('gzuncompress')) { $SWFfileData = $SWFHead.$decompressed;
throw new getid3_exception('getid3_swf requires --zlib support in PHP.');
}
if ($getid3->info['swf']['header']['compressed']) { } else {
if ($uncompressed_file_data = @gzuncompress(substr($swf_file_data, 8))) { $ThisFileInfo['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($ThisFileInfo['swf']['header']['length'] - 8).' bytes uncompressed)';
$swf_file_data = substr($swf_file_data, 0, 8).$uncompressed_file_data; return false;
} else { }
throw new getid3_exception('Error decompressing compressed SWF data');
}
} }
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
$frame_size_bits_per_value = (ord(substr($swf_file_data, 8, 1)) & 0xF8) >> 3; $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
$frame_size_data_length = ceil((5 + (4 * $frame_size_bits_per_value)) / 8); $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
$frame_size_data_string = str_pad(decbin(ord($swf_file_data[8]) & 0x07), 3, '0', STR_PAD_LEFT); $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);
for ($i = 1; $i < $frame_size_data_length; $i++) { // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
$frame_size_data_string .= str_pad(decbin(ord(substr($swf_file_data, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); // 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.
list($x1, $x2, $y1, $y2) = explode("\n", wordwrap($frame_size_data_string, $frame_size_bits_per_value, "\n", 1)); // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
$getid3->info['swf']['header']['frame_width'] = bindec($x2); $ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
$getid3->info['swf']['header']['frame_height'] = bindec($y2); $ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
// http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate'];
// Next in the header is the frame rate, which is kind of weird. $ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20));
// It is supposed to be stored as a 16bit integer, but the first byte $ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20));
// (or last depending on how you look at it) is completely ignored. $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
// Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
// Byte at (8 + $frame_size_data_length) is always zero and ignored if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) {
$getid3->info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int($swf_file_data[9 + $frame_size_data_length]); $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate'];
$getid3->info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($swf_file_data, 10 + $frame_size_data_length, 2)); }
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
$getid3->info['video']['frame_rate'] = $getid3->info['swf']['header']['frame_rate'];
$getid3->info['video']['resolution_x'] = intval(round($getid3->info['swf']['header']['frame_width'] / 20));
$getid3->info['video']['resolution_y'] = intval(round($getid3->info['swf']['header']['frame_height'] / 20));
$getid3->info['video']['pixel_aspect_ratio'] = (float)1;
if (($getid3->info['swf']['header']['frame_count'] > 0) && ($getid3->info['swf']['header']['frame_rate'] > 0)) {
$getid3->info['playtime_seconds'] = $getid3->info['swf']['header']['frame_count'] / $getid3->info['swf']['header']['frame_rate'];
}
// SWF tags // SWF tags
$current_offset = 12 + $frame_size_data_length; $CurrentOffset = 12 + $FrameSizeDataLength;
$swf_data_length = strlen($swf_file_data); $SWFdataLength = strlen($SWFfileData);
while ($current_offset < $swf_data_length) { while ($CurrentOffset < $SWFdataLength) {
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
$tag_ID_tag_length = getid3_lib::LittleEndian2Int(substr($swf_file_data, $current_offset, 2)); $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
$tag_ID = ($tag_ID_tag_length & 0xFFFC) >> 6; $TagID = ($TagIDTagLength & 0xFFFC) >> 6;
$tag_length = ($tag_ID_tag_length & 0x003F); $TagLength = ($TagIDTagLength & 0x003F);
$current_offset += 2; $CurrentOffset += 2;
if ($tag_length == 0x3F) { if ($TagLength == 0x3F) {
$tag_length = getid3_lib::LittleEndian2Int(substr($swf_file_data, $current_offset, 4)); $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
$current_offset += 4; $CurrentOffset += 4;
} }
unset($tag_data); unset($TagData);
$tag_data['offset'] = $current_offset; $TagData['offset'] = $CurrentOffset;
$tag_data['size'] = $tag_length; $TagData['size'] = $TagLength;
$tag_data['id'] = $tag_ID; $TagData['id'] = $TagID;
$tag_data['data'] = substr($swf_file_data, $current_offset, $tag_length); $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
switch ($tag_ID) { switch ($TagID) {
case 0: // end of movie
break 2;
case 0: // end of movie case 9: // Set background color
break 2; //$ThisFileInfo['swf']['tags'][] = $TagData;
$ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
break;
case 9: // Set background color default:
$getid3->info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($tag_data['data'])), 6, '0', STR_PAD_LEFT)); if ($ReturnAllTagData) {
break; $ThisFileInfo['swf']['tags'][] = $TagData;
}
break;
}
default: $CurrentOffset += $TagLength;
/* }
if ($ReturnAllTagData) {
$getid3->info['swf']['tags'][] = $tag_data;
}
*/
break;
}
$current_offset += $tag_length; return true;
} }
return true;
}
} }

View file

@ -0,0 +1,58 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.aa.php //
// module for analyzing Audible Audiobook files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_aa
{
function getid3_aa(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AAheader = fread($fd, 8);
$magic = "\x57\x90\x75\x36";
if (substr($AAheader, 4, 4) != $magic) {
$ThisFileInfo['error'][] = 'Expecting "'.PrintHexBytes($magic).'" at offset '.$ThisFileInfo['avdataoffset'].', found "'.PrintHexBytes(substr($AAheader, 4, 4)).'"';
return false;
}
// shortcut
$ThisFileInfo['aa'] = array();
$thisfile_au = &$ThisFileInfo['aa'];
$ThisFileInfo['fileformat'] = 'aa';
$ThisFileInfo['audio']['dataformat'] = 'aa';
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; // is it?
$thisfile_au['encoding'] = 'ISO-8859-1';
$thisfile_au['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4));
if ($thisfile_au['filesize'] > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) {
$ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['filesize'].'" bytes of data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"';
}
$ThisFileInfo['audio']['bits_per_sample'] = 16; // is it?
$ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate'];
$ThisFileInfo['audio']['channels'] = $thisfile_au['channels'];
//$ThisFileInfo['playtime_seconds'] = 0;
//$ThisFileInfo['audio']['bitrate'] = 0;
return true;
}
}
?>

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

View file

@ -1,500 +1,496 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.ac3.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing AC-3 (aka Dolby Digital) audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.ac3.php |
// | Module for analyzing AC-3 (aka Dolby Digital) audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.ac3.php,v 1.3 2006/11/02 10:48:01 ah Exp $
class getid3_ac3
class getid3_ac3 extends getid3_handler
{ {
public function Analyze() { function getid3_ac3(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; ///AH
$ThisFileInfo['ac3']['raw']['bsi'] = array();
// http://www.atsc.org/standards/a_52a.pdf $thisfile_ac3 = &$ThisFileInfo['ac3'];
$thisfile_ac3_raw = &$thisfile_ac3['raw'];
$getid3->info['fileformat'] = 'ac3'; $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
$getid3->info['audio']['dataformat'] = 'ac3';
$getid3->info['audio']['bitrate_mode'] = 'cbr';
$getid3->info['audio']['lossless'] = false; // http://www.atsc.org/standards/a_52a.pdf
$getid3->info['ac3']['raw']['bsi'] = array (); $ThisFileInfo['fileformat'] = 'ac3';
$info_ac3 = &$getid3->info['ac3']; $ThisFileInfo['audio']['dataformat'] = 'ac3';
$info_ac3_raw = &$info_ac3['raw']; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$info_ac3_raw_bsi = &$info_ac3_raw['bsi']; $ThisFileInfo['audio']['lossless'] = false;
// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
// 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
// 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
// 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
// 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
// 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
// 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
// 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.
// 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
// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$this->fseek($getid3->info['avdataoffset'], SEEK_SET); $AC3header['syncinfo'] = fread($fd, 5);
$ac3_header['syncinfo'] = $this->fread(5); $thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2);
$info_ac3_raw['synchinfo']['synchword'] = substr($ac3_header['syncinfo'], 0, 2);
if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") {
if ($info_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") {
throw new getid3_exception('Expecting "\x0B\x77" at offset '.$getid3->info['avdataoffset'].', found \x'.strtoupper(dechex($ac3_header['syncinfo']{0})).'\x'.strtoupper(dechex($ac3_header['syncinfo']{1})).' instead'); $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;
// syncinfo() { } else {
// syncword 16
// crc1 16 // syncinfo() {
// fscod 2 // syncword 16
// frmsizecod 6 // crc1 16
// } /* end of syncinfo */ // fscod 2
// frmsizecod 6
$info_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 2, 2)); // } /* end of syncinfo */
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 4, 1));
$info_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6; $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2));
$info_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F); $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 4, 1));
$thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
$info_ac3['sample_rate'] = getid3_ac3::AC3sampleRateCodeLookup($info_ac3_raw['synchinfo']['fscod']); $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
if ($info_ac3_raw['synchinfo']['fscod'] <= 3) {
$getid3->info['audio']['sample_rate'] = $info_ac3['sample_rate']; $thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
} if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
$ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
$info_ac3['frame_length'] = getid3_ac3::AC3frameSizeLookup($info_ac3_raw['synchinfo']['frmsizecod'], $info_ac3_raw['synchinfo']['fscod']); }
$info_ac3['bitrate'] = getid3_ac3::AC3bitrateLookup($info_ac3_raw['synchinfo']['frmsizecod']);
$getid3->info['audio']['bitrate'] = $info_ac3['bitrate']; $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']);
$ac3_header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15)); $ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate'];
$info_ac3_raw_bsi['bsid'] = bindec(substr($ac3_header['bsi'], 0, 5)); $AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15));
if ($info_ac3_raw_bsi['bsid'] > 8) { $ac3_bsi_offset = 0;
// 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. $thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
// Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. $ac3_bsi_offset += 5;
throw new getid3_exception('Bit stream identification is version '.$info_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'); 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.
$info_ac3_raw_bsi['bsmod'] = bindec(substr($ac3_header['bsi'], 5, 3)); // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
$info_ac3_raw_bsi['acmod'] = bindec(substr($ac3_header['bsi'], 8, 3)); $ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8';
unset($thisfile_ac3);
$info_ac3['service_type'] = getid3_ac3::AC3serviceTypeLookup($info_ac3_raw_bsi['bsmod'], $info_ac3_raw_bsi['acmod']); return false;
$ac3_coding_mode = getid3_ac3::AC3audioCodingModeLookup($info_ac3_raw_bsi['acmod']); }
foreach($ac3_coding_mode as $key => $value) {
$info_ac3[$key] = $value; $thisfile_ac3_raw_bsi['bsmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
} $ac3_bsi_offset += 3;
switch ($info_ac3_raw_bsi['acmod']) { $thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
case 0: $ac3_bsi_offset += 3;
case 1:
$getid3->info['audio']['channelmode'] = 'mono'; $thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
break; $ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
case 3: foreach($ac3_coding_mode as $key => $value) {
case 4: $thisfile_ac3[$key] = $value;
$getid3->info['audio']['channelmode'] = 'stereo'; }
break; switch ($thisfile_ac3_raw_bsi['acmod']) {
default: case 0:
$getid3->info['audio']['channelmode'] = 'surround'; case 1:
break; $ThisFileInfo['audio']['channelmode'] = 'mono';
} break;
$getid3->info['audio']['channels'] = $info_ac3['num_channels']; case 3:
case 4:
$offset = 11; $ThisFileInfo['audio']['channelmode'] = 'stereo';
break;
if ($info_ac3_raw_bsi['acmod'] & 0x01) { default:
// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. $ThisFileInfo['audio']['channelmode'] = 'surround';
$info_ac3_raw_bsi['cmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2)); break;
$info_ac3['center_mix_level'] = getid3_ac3::AC3centerMixLevelLookup($info_ac3_raw_bsi['cmixlev']); }
$offset += 2; $ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels'];
}
if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
if ($info_ac3_raw_bsi['acmod'] & 0x04) { // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. $thisfile_ac3_raw_bsi['cmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$info_ac3_raw_bsi['surmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $ac3_bsi_offset += 2;
$info_ac3['surround_mix_level'] = getid3_ac3::AC3surroundMixLevelLookup($info_ac3_raw_bsi['surmixlev']); $thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
$offset += 2; }
}
if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
if ($info_ac3_raw_bsi['acmod'] == 0x02) { // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
// 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['surmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
$info_ac3_raw_bsi['dsurmod'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $ac3_bsi_offset += 2;
$info_ac3['dolby_surround_mode'] = getid3_ac3::AC3dolbySurroundModeLookup($info_ac3_raw_bsi['dsurmod']); $thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
$offset += 2; }
}
if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
$info_ac3_raw_bsi['lfeon'] = $ac3_header['bsi']{$offset++} == '1'; // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
$info_ac3['lfe_enabled'] = $info_ac3_raw_bsi['lfeon']; $thisfile_ac3_raw_bsi['dsurmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
if ($info_ac3_raw_bsi['lfeon']) { $ac3_bsi_offset += 2;
$getid3->info['audio']['channels'] .= '.1'; $thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
} }
$info_ac3['channels_enabled'] = getid3_ac3::AC3channelsEnabledLookup($info_ac3_raw_bsi['acmod'], $info_ac3_raw_bsi['lfeon']); $thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$ac3_bsi_offset += 1;
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131. $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
// 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. if ($thisfile_ac3_raw_bsi['lfeon']) {
$info_ac3_raw_bsi['dialnorm'] = bindec(substr($ac3_header['bsi'], $offset, 5)); //$ThisFileInfo['audio']['channels']++;
$offset += 5; $ThisFileInfo['audio']['channels'] .= '.1';
$info_ac3['dialogue_normalization'] = '-'.$info_ac3_raw_bsi['dialnorm'].'dB'; }
$info_ac3_raw_bsi['compre_flag'] = $ac3_header['bsi']{$offset++} == '1'; $thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
if ($info_ac3_raw_bsi['compre_flag']) {
$info_ac3_raw_bsi['compr'] = bindec(substr($ac3_header['bsi'], $offset, 8)); // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131.
$offset += 8; // 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));
$info_ac3['heavy_compression'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr']); $ac3_bsi_offset += 5;
} $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
$info_ac3_raw_bsi['langcode_flag'] = $ac3_header['bsi']{$offset++} == '1'; $thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
if ($info_ac3_raw_bsi['langcode_flag']) { $ac3_bsi_offset += 1;
$info_ac3_raw_bsi['langcod'] = bindec(substr($ac3_header['bsi'], $offset, 8)); if ($thisfile_ac3_raw_bsi['compre_flag']) {
$offset += 8; $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']);
$info_ac3_raw_bsi['audprodie'] = $ac3_header['bsi']{$offset++} == '1'; }
if ($info_ac3_raw_bsi['audprodie']) {
$info_ac3_raw_bsi['mixlevel'] = bindec(substr($ac3_header['bsi'], $offset, 5)); $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
$offset += 5; $ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['langcode_flag']) {
$info_ac3_raw_bsi['roomtyp'] = bindec(substr($ac3_header['bsi'], $offset, 2)); $thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
$offset += 2; $ac3_bsi_offset += 8;
}
$info_ac3['mixing_level'] = (80 + $info_ac3_raw_bsi['mixlevel']).'dB';
$info_ac3['room_type'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp']); $thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
} $ac3_bsi_offset += 1;
if ($thisfile_ac3_raw_bsi['audprodie']) {
if ($info_ac3_raw_bsi['acmod'] == 0x00) { $thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
// If acmod is 0, then two completely independent program channels (dual mono) $ac3_bsi_offset += 5;
// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, $thisfile_ac3_raw_bsi['roomtyp'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
// a number of additional items are present in BSI or audblk to fully describe Ch2. $ac3_bsi_offset += 2;
$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131. $thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
// 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. }
$info_ac3_raw_bsi['dialnorm2'] = bindec(substr($ac3_header['bsi'], $offset, 5));
$offset += 5; if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
// If acmod is 0, then two completely independent program channels (dual mono)
$info_ac3['dialogue_normalization2'] = '-'.$info_ac3_raw_bsi['dialnorm2'].'dB'; // 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.
$info_ac3_raw_bsi['compre_flag2'] = $ac3_header['bsi']{$offset++} == '1';
if ($info_ac3_raw_bsi['compre_flag2']) {
$info_ac3_raw_bsi['compr2'] = bindec(substr($ac3_header['bsi'], $offset, 8)); // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131.
$offset += 8; // 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));
$info_ac3['heavy_compression2'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr2']); $ac3_bsi_offset += 5;
} $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
$info_ac3_raw_bsi['langcode_flag2'] = $ac3_header['bsi']{$offset++} == '1'; $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
if ($info_ac3_raw_bsi['langcode_flag2']) { $ac3_bsi_offset += 1;
$info_ac3_raw_bsi['langcod2'] = bindec(substr($ac3_header['bsi'], $offset, 8)); if ($thisfile_ac3_raw_bsi['compre_flag2']) {
$offset += 8; $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);
}
$info_ac3_raw_bsi['audprodie2'] = $ac3_header['bsi']{$offset++} == '1';
if ($info_ac3_raw_bsi['audprodie2']) {
$info_ac3_raw_bsi['mixlevel2'] = bindec(substr($ac3_header['bsi'], $offset, 5));
$offset += 5;
$info_ac3_raw_bsi['roomtyp2'] = bindec(substr($ac3_header['bsi'], $offset, 2));
$offset += 2;
$info_ac3['mixing_level2'] = (80 + $info_ac3_raw_bsi['mixlevel2']).'dB';
$info_ac3['room_type2'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp2']);
}
}
$info_ac3_raw_bsi['copyright'] = $ac3_header['bsi']{$offset++} == '1';
$info_ac3_raw_bsi['original'] = $ac3_header['bsi']{$offset++} == '1';
$info_ac3_raw_bsi['timecode1_flag'] = $ac3_header['bsi']{$offset++} == '1';
if ($info_ac3_raw_bsi['timecode1_flag']) {
$info_ac3_raw_bsi['timecode1'] = bindec(substr($ac3_header['bsi'], $offset, 14));
$offset += 14;
}
$info_ac3_raw_bsi['timecode2_flag'] = $ac3_header['bsi']{$offset++} == '1';
if ($info_ac3_raw_bsi['timecode2_flag']) {
$info_ac3_raw_bsi['timecode2'] = bindec(substr($ac3_header['bsi'], $offset, 14));
$offset += 14;
}
$info_ac3_raw_bsi['addbsi_flag'] = $ac3_header['bsi']{$offset++} == '1';
if ($info_ac3_raw_bsi['addbsi_flag']) {
$info_ac3_raw_bsi['addbsi_length'] = bindec(substr($ac3_header['bsi'], $offset, 6));
$offset += 6;
$ac3_header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($info_ac3_raw_bsi['addbsi_length']));
$info_ac3_raw_bsi['addbsi_data'] = substr($ac3_header['bsi'], 119, $info_ac3_raw_bsi['addbsi_length'] * 8);
}
return true;
}
public static function AC3sampleRateCodeLookup($fscod) {
static $lookup = 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($lookup[$fscod]) ? $lookup[$fscod] : false);
}
public static function AC3serviceTypeLookup($bsmod, $acmod) {
static $lookup = array (
0 => 'main audio service: complete main (CM)',
1 => 'main audio service: music and effects (ME)',
2 => 'associated service: visually impaired (VI)',
3 => 'associated service: hearing impaired (HI)',
4 => 'associated service: dialogue (D)',
5 => 'associated service: commentary (C)',
6 => 'associated service: emergency (E)',
7 => 'main audio service: karaoke'
);
if ($bsmod == 7 && $acmod == 1) {
return 'associated service: voice over (VO)';
}
return (isset($lookup[$bsmod]) ? $lookup[$bsmod] : false);
}
public static function AC3audioCodingModeLookup($acmod) {
// array (channel configuration, # channels (not incl LFE), channel order)
static $lookup = 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($lookup[$acmod]) ? $lookup[$acmod] : false);
}
public static function AC3centerMixLevelLookup($cmixlev) {
static $lookup;
if (!@$lookup) {
$lookup = 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($lookup[$cmixlev]) ? $lookup[$cmixlev] : false);
}
public static function AC3surroundMixLevelLookup($surmixlev) {
static $lookup;
if (!@$lookup) {
$lookup = array (
0 => pow(2, -3.0 / 6),
1 => pow(2, -6.0 / 6),
2 => 0,
3 => 'reserved'
);
}
return (isset($lookup[$surmixlev]) ? $lookup[$surmixlev] : false);
}
public static function AC3dolbySurroundModeLookup($dsurmod) {
static $lookup = array (
0 => 'not indicated',
1 => 'Not Dolby Surround encoded',
2 => 'Dolby Surround encoded',
3 => 'reserved'
);
return (isset($lookup[$dsurmod]) ? $lookup[$dsurmod] : false);
}
public static function AC3channelsEnabledLookup($acmod, $lfeon) {
return array (
'ch1' => $acmod == 0,
'ch2' => $acmod == 0,
'left' => $acmod > 1,
'right' => $acmod > 1,
'center' => (bool)($acmod & 0x01),
'surround_mono' => $acmod == 4 || $acmod == 5,
'surround_left' => $acmod == 6 || $acmod == 7,
'surround_right' => $acmod == 6 || $acmod == 7,
'lfe' => $lfeon
);
}
public static function AC3heavyCompression($compre) {
// The first four bits indicate gain changes in 6.02dB increments which can be
// implemented with an arithmetic shift operation. The following four bits
// indicate linear gain changes, and require a 5-bit multiply.
// We will represent the two 4-bit fields of compr as follows:
// X0 X1 X2 X3 . Y4 Y5 Y6 Y7
// The meaning of the X values is most simply described by considering X to represent a 4-bit
// signed integer with values from 8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
// following table shows this in detail.
// Meaning of 4 msb of compr
// 7 +48.16 dB
// 6 +42.14 dB
// 5 +36.12 dB
// 4 +30.10 dB
// 3 +24.08 dB
// 2 +18.06 dB
// 1 +12.04 dB
// 0 +6.02 dB
// -1 0 dB
// -2 6.02 dB
// -3 12.04 dB
// -4 18.06 dB
// -5 24.08 dB
// -6 30.10 dB
// -7 36.12 dB
// -8 42.14 dB
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
if ($fourbit{0} == '1') {
$log_gain = -8 + bindec(substr($fourbit, 1));
} else {
$log_gain = bindec(substr($fourbit, 1));
}
$log_gain = ($log_gain + 1) * (20 * log10(2));
// The value of Y is a linear representation of a gain change of up to 6 dB. Y is considered to
// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
// changes from 0.28 dB to 6.02 dB.
$lin_gain = (16 + ($compre & 0x0F)) / 32;
// The combination of X and Y values allows compr to indicate gain changes from
// 48.16 0.28 = +47.89 dB, to
// 42.14 6.02 = 48.16 dB.
return $log_gain - $lin_gain;
}
public static function AC3roomTypeLookup($roomtyp) {
static $lookup = array (
0 => 'not indicated',
1 => 'large room, X curve monitor',
2 => 'small room, flat monitor',
3 => 'reserved'
);
return (isset($lookup[$roomtyp]) ? $lookup[$roomtyp] : false);
}
public static function AC3frameSizeLookup($frmsizecod, $fscod) {
$padding = (bool)($frmsizecod % 2);
$frame_size_id = floor($frmsizecod / 2);
static $lookup = 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
$lookup[$frmsizecod] += 2;
}
return (isset($lookup[$frame_size_id][$fscod]) ? $lookup[$frame_size_id][$fscod] : false);
}
public static function AC3bitrateLookup($frmsizecod) {
static $lookup = 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
);
$frame_size_id = floor($frmsizecod / 2);
return (isset($lookup[$frame_size_id]) ? $lookup[$frame_size_id] : false);
}
} }

View file

@ -1,183 +1,161 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.au.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing AU files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.au.php |
// | module for analyzing AU files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.au.php,v 1.2 2006/11/02 10:48:01 ah Exp $
class getid3_au
class getid3_au extends getid3_handler
{ {
public function Analyze() { function getid3_au(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$AUheader = fread($fd, 8);
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); if (substr($AUheader, 0, 4) != '.snd') {
$au_header = fread($getid3->fp, 8); $ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"';
return false;
}
// Magic bytes: .snd // shortcut
$ThisFileInfo['au'] = array();
$thisfile_au = &$ThisFileInfo['au'];
$getid3->info['au'] = array (); $ThisFileInfo['fileformat'] = 'au';
$info_au = &$getid3->info['au']; $ThisFileInfo['audio']['dataformat'] = 'au';
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$thisfile_au['encoding'] = 'ISO-8859-1';
$getid3->info['fileformat'] = 'au'; $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
$getid3->info['audio']['dataformat'] = 'au'; $AUheader .= fread($fd, $thisfile_au['header_length'] - 8);
$getid3->info['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['avdataoffset'] += $thisfile_au['header_length'];
$info_au['encoding'] = 'ISO-8859-1';
$info_au['header_length'] = getid3_lib::BigEndian2Int(substr($au_header, 4, 4)); $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
$au_header .= fread($getid3->fp, $info_au['header_length'] - 8); $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
$getid3->info['avdataoffset'] += $info_au['header_length']; $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));
getid3_lib::ReadSequence('BigEndian2Int', $info_au, $au_header, 8, $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
array ( $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
'data_size' => 4, if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
'data_format_id'=> 4, $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
'sample_rate' => 4, } else {
'channels' => 4 unset($thisfile_au['bits_per_sample']);
) }
);
$info_au['comments']['comment'][] = trim(substr($au_header, 24));
$info_au['data_format'] = getid3_au::AUdataFormatNameLookup($info_au['data_format_id']); $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate'];
$info_au['used_bits_per_sample'] = getid3_au::AUdataFormatUsedBitsPerSampleLookup($info_au['data_format_id']); $ThisFileInfo['audio']['channels'] = $thisfile_au['channels'];
if ($info_au['bits_per_sample'] = getid3_au::AUdataFormatBitsPerSampleLookup($info_au['data_format_id'])) {
$getid3->info['audio']['bits_per_sample'] = $info_au['bits_per_sample'];
} else {
unset($info_au['bits_per_sample']);
}
$getid3->info['audio']['sample_rate'] = $info_au['sample_rate']; if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) {
$getid3->info['audio']['channels'] = $info_au['channels']; $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"';
}
if (($getid3->info['avdataoffset'] + $info_au['data_size']) > $getid3->info['avdataend']) { $ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
$getid3->warning('Possible truncated file - expecting "'.$info_au['data_size'].'" bytes of audio data, only found '.($getid3->info['avdataend'] - $getid3->info['avdataoffset']).' bytes"'); $ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds'];
}
$getid3->info['playtime_seconds'] = $info_au['data_size'] / ($info_au['sample_rate'] * $info_au['channels'] * ($info_au['used_bits_per_sample'] / 8)); return true;
$getid3->info['audio']['bitrate'] = ($info_au['data_size'] * 8) / $getid3->info['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,
public static function AUdataFormatNameLookup($id) { 11 => 8,
12 => 16,
13 => 24,
14 => 32,
static $lookup = array ( 18 => 16,
0 => 'unspecified format', 19 => 16,
1 => '8-bit mu-law', 20 => 16,
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', 23 => 16,
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($lookup[$id]) ? $lookup[$id] : false); 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,
public static function AUdataFormatBitsPerSampleLookup($id) { 18 => 16,
19 => 16,
20 => 16,
static $lookup = array ( 23 => 4,
1 => 8,
2 => 8,
3 => 16,
4 => 24,
5 => 32,
6 => 32,
7 => 64,
11 => 8, 25 => 3,
12 => 16, 26 => 5,
13 => 24, 27 => 8,
14 => 32, );
return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
18 => 16, }
19 => 16,
20 => 16,
23 => 16,
25 => 16,
26 => 16,
27 => 8
);
return (isset($lookup[$id]) ? $lookup[$id] : false);
}
public static function AUdataFormatUsedBitsPerSampleLookup($id) {
static $lookup = 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($lookup[$id]) ? $lookup[$id] : false);
}
} }

View file

@ -1,135 +1,124 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.avr.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing AVR Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.avr.php |
// | Module for analyzing AVR audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.avr.php,v 1.2 2006/11/02 10:48:01 ah Exp $
class getid3_avr
class getid3_avr extends getid3_handler
{ {
public function Analyze() { function getid3_avr(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; // 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)
// ---------------------------------------------------------------------
// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html // Note that all values are in motorola (big-endian) format, and that long is
// http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html // assumed to be 4 bytes, and short 2 bytes.
// offset type length name comments // 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
// 0 char 4 ID format ID == "2BIT" // 8-bit data between signed/unsigned just add 127 to the sample values.
// 4 char 8 name sample name (unused space filled with 0) // Simularly for 16-bit data you should add 32769
// 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 $ThisFileInfo['fileformat'] = 'avr';
// assumed to be 4 bytes, and short 2 bytes.
// When reading the samples, you should handle both signed and unsigned data, fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
// and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert $AVRheader = fread($fd, 128);
// 8-bit data between signed/unsigned just add 127 to the sample values.
// Simularly for 16-bit data you should add 32769 $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'];
// Magic bytes: '2BIT' return true;
}
$getid3->info['avr'] = array ();
$info_avr = &$getid3->info['avr'];
$getid3->info['fileformat'] = 'avr';
$info_avr['raw']['magic'] = '2BIT';
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
$avr_header = fread($getid3->fp, 128);
$getid3->info['avdataoffset'] += 128;
$info_avr['sample_name'] = rtrim(substr($avr_header, 4, 8));
$info_avr['raw']['mono'] = getid3_lib::BigEndian2Int(substr($avr_header, 12, 2));
$info_avr['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($avr_header, 14, 2));
$info_avr['raw']['signed'] = getid3_lib::BigEndian2Int(substr($avr_header, 16, 2));
$info_avr['raw']['loop'] = getid3_lib::BigEndian2Int(substr($avr_header, 18, 2));
$info_avr['raw']['midi'] = getid3_lib::BigEndian2Int(substr($avr_header, 20, 2));
$info_avr['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($avr_header, 22, 1));
$info_avr['sample_rate'] = getid3_lib::BigEndian2Int(substr($avr_header, 23, 3));
$info_avr['sample_length'] = getid3_lib::BigEndian2Int(substr($avr_header, 26, 4));
$info_avr['loop_start'] = getid3_lib::BigEndian2Int(substr($avr_header, 30, 4));
$info_avr['loop_end'] = getid3_lib::BigEndian2Int(substr($avr_header, 34, 4));
$info_avr['midi_split'] = getid3_lib::BigEndian2Int(substr($avr_header, 38, 2));
$info_avr['sample_compression'] = getid3_lib::BigEndian2Int(substr($avr_header, 40, 2));
$info_avr['reserved'] = getid3_lib::BigEndian2Int(substr($avr_header, 42, 2));
$info_avr['sample_name_extra'] = rtrim(substr($avr_header, 44, 20));
$info_avr['comment'] = rtrim(substr($avr_header, 64, 64));
$info_avr['flags']['stereo'] = (($info_avr['raw']['mono'] == 0) ? false : true);
$info_avr['flags']['signed'] = (($info_avr['raw']['signed'] == 0) ? false : true);
$info_avr['flags']['loop'] = (($info_avr['raw']['loop'] == 0) ? false : true);
$info_avr['midi_notes'] = array ();
if (($info_avr['raw']['midi'] & 0xFF00) != 0xFF00) {
$info_avr['midi_notes'][] = ($info_avr['raw']['midi'] & 0xFF00) >> 8;
}
if (($info_avr['raw']['midi'] & 0x00FF) != 0x00FF) {
$info_avr['midi_notes'][] = ($info_avr['raw']['midi'] & 0x00FF);
}
if (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) != ($info_avr['sample_length'] * (($info_avr['bits_per_sample'] == 8) ? 1 : 2))) {
$getid3->warning('Probable truncated file: expecting '.($info_avr['sample_length'] * (($info_avr['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($getid3->info['avdataend'] - $getid3->info['avdataoffset']));
}
$getid3->info['audio']['dataformat'] = 'avr';
$getid3->info['audio']['lossless'] = true;
$getid3->info['audio']['bitrate_mode'] = 'cbr';
$getid3->info['audio']['bits_per_sample'] = $info_avr['bits_per_sample'];
$getid3->info['audio']['sample_rate'] = $info_avr['sample_rate'];
$getid3->info['audio']['channels'] = ($info_avr['flags']['stereo'] ? 2 : 1);
$getid3->info['playtime_seconds'] = ($info_avr['sample_length'] / $getid3->info['audio']['channels']) / $info_avr['sample_rate'];
$getid3->info['audio']['bitrate'] = ($info_avr['sample_length'] * (($info_avr['bits_per_sample'] == 8) ? 8 : 16)) / $getid3->info['playtime_seconds'];
return true;
}
} }

View file

@ -1,234 +1,219 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.la.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing BONK audio files //
// +----------------------------------------------------------------------+ // dependencies: module.tag.id3v2.php (optional) //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.bonk.php |
// | Module for analyzing BONK audio files |
// | dependencies: module.tag.id3v2.php (optional) |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.bonk.php,v 1.3 2006/11/02 10:48:01 ah Exp $
class getid3_bonk
class getid3_bonk extends getid3_handler
{ {
function getid3_bonk(&$fd, &$ThisFileInfo) {
public function Analyze() { // shortcut
$ThisFileInfo['bonk'] = array();
$thisfile_bonk = &$ThisFileInfo['bonk'];
$getid3 = $this->getid3; $thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset'];
$thisfile_bonk['dataend'] = $ThisFileInfo['avdataend'];
$getid3->info['bonk'] = array (); if ($thisfile_bonk['dataend'] >= pow(2, 31)) {
$info_bonk = &$getid3->info['bonk'];
$info_bonk['dataoffset'] = $getid3->info['avdataoffset']; $ThisFileInfo['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to 2GB';
$info_bonk['dataend'] = $getid3->info['avdataend'];
} else {
// Scan-from-end method, for v0.6 and higher // scan-from-end method, for v0.6 and higher
fseek($getid3->fp, $info_bonk['dataend'] - 8, SEEK_SET); fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET);
$possible_bonk_tag = fread($getid3->fp, 8); $PossibleBonkTag = fread($fd, 8);
while (getid3_bonk::BonkIsValidTagName(substr($possible_bonk_tag, 4, 4), true)) { while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
$bonk_tag_size = getid3_lib::LittleEndian2Int(substr($possible_bonk_tag, 0, 4)); $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
fseek($getid3->fp, 0 - $bonk_tag_size, SEEK_CUR); fseek($fd, 0 - $BonkTagSize, SEEK_CUR);
$bonk_tag_offset = ftell($getid3->fp); $BonkTagOffset = ftell($fd);
$tag_header_test = fread($getid3->fp, 5); $TagHeaderTest = fread($fd, 5);
if (($tag_header_test{0} != "\x00") || (substr($possible_bonk_tag, 4, 4) != strtolower(substr($possible_bonk_tag, 4, 4)))) { if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
throw new getid3_exception('Expecting "Ø'.strtoupper(substr($possible_bonk_tag, 4, 4)).'" at offset '.$bonk_tag_offset.', found "'.$tag_header_test.'"'); $ThisFileInfo['error'][] = 'Expecting "Ø'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"';
} return false;
$bonk_tag_name = substr($tag_header_test, 1, 4); }
$BonkTagName = substr($TagHeaderTest, 1, 4);
$info_bonk[$bonk_tag_name]['size'] = $bonk_tag_size; $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
$info_bonk[$bonk_tag_name]['offset'] = $bonk_tag_offset; $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
$this->HandleBonkTags($bonk_tag_name); $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);
}
$next_tag_end_offset = $bonk_tag_offset - 8; }
if ($next_tag_end_offset < $info_bonk['dataoffset']) {
if (empty($getid3->info['audio']['encoder'])) {
$getid3->info['audio']['encoder'] = 'Extended BONK v0.9+';
}
return true;
}
fseek($getid3->fp, $next_tag_end_offset, SEEK_SET);
$possible_bonk_tag = fread($getid3->fp, 8);
}
// Seek-from-beginning method for v0.4 and v0.5 // seek-from-beginning method for v0.4 and v0.5
if (empty($info_bonk['BONK'])) { if (empty($thisfile_bonk['BONK'])) {
fseek($getid3->fp, $info_bonk['dataoffset'], SEEK_SET); fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET);
do { do {
$tag_header_test = fread($getid3->fp, 5); $TagHeaderTest = fread($fd, 5);
switch ($tag_header_test) { switch ($TagHeaderTest) {
case "\x00".'BONK': case "\x00".'BONK':
if (empty($getid3->info['audio']['encoder'])) { if (empty($ThisFileInfo['audio']['encoder'])) {
$getid3->info['audio']['encoder'] = 'BONK v0.4'; $ThisFileInfo['audio']['encoder'] = 'BONK v0.4';
} }
break; break;
case "\x00".'INFO': case "\x00".'INFO':
$getid3->info['audio']['encoder'] = 'Extended BONK v0.5'; $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5';
break; break;
default: default:
break 2; break 2;
} }
$bonk_tag_name = substr($tag_header_test, 1, 4); $BonkTagName = substr($TagHeaderTest, 1, 4);
$info_bonk[$bonk_tag_name]['size'] = $info_bonk['dataend'] - $info_bonk['dataoffset']; $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
$info_bonk[$bonk_tag_name]['offset'] = $info_bonk['dataoffset']; $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
$this->HandleBonkTags($bonk_tag_name); $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
} while (true); } 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';
// Parse META block for v0.6 - v0.8 $BonkTagName = substr($TagHeaderTest, 1, 4);
if (!@$info_bonk['INFO'] && isset($info_bonk['META']['tags']['info'])) { $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
fseek($getid3->fp, $info_bonk['META']['tags']['info'], SEEK_SET); $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
$tag_header_test = fread($getid3->fp, 5); $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
if ($tag_header_test == "\x00".'INFO') { }
$getid3->info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; }
$bonk_tag_name = substr($tag_header_test, 1, 4); if (empty($ThisFileInfo['audio']['encoder'])) {
$info_bonk[$bonk_tag_name]['size'] = $info_bonk['dataend'] - $info_bonk['dataoffset']; $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
$info_bonk[$bonk_tag_name]['offset'] = $info_bonk['dataoffset']; }
$this->HandleBonkTags($bonk_tag_name); if (empty($thisfile_bonk['BONK'])) {
} unset($ThisFileInfo['bonk']);
} }
return true;
if (empty($getid3->info['audio']['encoder'])) { }
$getid3->info['audio']['encoder'] = 'Extended BONK v0.9+';
}
if (empty($info_bonk['BONK'])) {
unset($getid3->info['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));
private function HandleBonkTags(&$bonk_tag_name) { $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));
// Shortcut to getid3 pointer $ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
$getid3 = $this->getid3; $ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
$info_audio = &$getid3->info['audio'];
switch ($bonk_tag_name) { $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';
case 'BONK': $ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
// shortcut if ($ThisFileInfo['playtime_seconds'] > 0) {
$info_bonk_BONK = &$getid3->info['bonk']['BONK']; $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
}
break;
$bonk_data = "\x00".'BONK'.fread($getid3->fp, 17); case 'INFO':
// shortcut
$thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO'];
getid3_lib::ReadSequence('LittleEndian2Int', $info_bonk_BONK, $bonk_data, 5, $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
array ( $thisfile_bonk_INFO['entries_count'] = 0;
'version' => 1, $NextInfoDataPair = fread($fd, 5);
'number_samples' => 4, if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
'sample_rate' => 4, while (!feof($fd)) {
'channels' => 1, //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
'lossless' => 1, //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
'joint_stereo' => 1, //$thisfile_bonk_INFO[] = $CurrentSeekInfo;
'number_taps' => 2,
'downsampling_ratio' => 1,
'samples_per_packet' => 2
)
);
$info_bonk_BONK['lossless'] = (bool)$info_bonk_BONK['lossless']; $NextInfoDataPair = fread($fd, 5);
$info_bonk_BONK['joint_stereo'] = (bool)$info_bonk_BONK['joint_stereo']; if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
fseek($fd, -5, SEEK_CUR);
break;
}
$thisfile_bonk_INFO['entries_count']++;
}
}
break;
$getid3->info['avdataoffset'] = $info_bonk_BONK['offset'] + 5 + 17; case 'META':
$getid3->info['avdataend'] = $info_bonk_BONK['offset'] + $info_bonk_BONK['size']; $BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5);
$ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
$getid3->info['fileformat'] = 'bonk'; $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
$info_audio['dataformat'] = 'bonk'; $offset = 6;
$info_audio['bitrate_mode'] = 'vbr'; // assumed for ($i = 0; $i < $MetaTagEntries; $i++) {
$info_audio['channels'] = $info_bonk_BONK['channels']; $MetaEntryTagName = substr($BonkData, $offset, 4);
$info_audio['sample_rate'] = $info_bonk_BONK['sample_rate']; $offset += 4;
$info_audio['channelmode'] = $info_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'; $MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
$info_audio['lossless'] = $info_bonk_BONK['lossless']; $offset += 4;
$info_audio['codec'] = 'bonk'; $ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
}
break;
$getid3->info['playtime_seconds'] = $info_bonk_BONK['number_samples'] / ($info_bonk_BONK['sample_rate'] * $info_bonk_BONK['channels']); case ' ID3':
if ($getid3->info['playtime_seconds'] > 0) { $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
$info_audio['bitrate'] = (($getid3->info['bonk']['dataend'] - $getid3->info['bonk']['dataoffset']) * 8) / $getid3->info['playtime_seconds'];
}
break;
case 'INFO': // ID3v2 checking is optional
// shortcut if (class_exists('getid3_id3v2')) {
$info_bonk_INFO = &$getid3->info['bonk']['INFO']; $ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2);
}
break;
$info_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($getid3->fp, 1)); default:
$info_bonk_INFO['entries_count'] = 0; $ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset'];
$next_info_data_pair = fread($getid3->fp, 5); break;
if (!getid3_bonk::BonkIsValidTagName(substr($next_info_data_pair, 1, 4))) {
while (!feof($getid3->fp)) {
$next_info_data_pair = fread($getid3->fp, 5);
if (getid3_bonk::BonkIsValidTagName(substr($next_info_data_pair, 1, 4))) {
fseek($getid3->fp, -5, SEEK_CUR);
break;
}
$info_bonk_INFO['entries_count']++;
}
}
break;
case 'META': }
$bonk_data = "\x00".'META'.fread($getid3->fp, $getid3->info['bonk']['META']['size'] - 5); }
$getid3->info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($bonk_data, 5, 1));
$meta_tag_entries = floor(((strlen($bonk_data) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
$offset = 6; static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
for ($i = 0; $i < $meta_tag_entries; $i++) { foreach ($BonkIsValidTagName as $validtagname) {
$meta_entry_tag_name = substr($bonk_data, $offset, 4); if ($validtagname == $PossibleBonkTag) {
$offset += 4; return true;
$meta_entry_tag_offset = getid3_lib::LittleEndian2Int(substr($bonk_data, $offset, 4)); } elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
$offset += 4; return true;
$getid3->info['bonk']['META']['tags'][$meta_entry_tag_name] = $meta_entry_tag_offset; }
} }
break; return false;
}
case ' ID3':
$info_audio['encoder'] = 'Extended BONK v0.9+';
// ID3v2 checking is optional
if (class_exists('getid3_id3v2')) {
$id3v2 = new getid3_id3v2($getid3);
$id3v2->option_starting_offset = $getid3->info['bonk'][' ID3']['offset'] + 2;
$getid3->info['bonk'][' ID3']['valid'] = $id3v2->Analyze();
}
break;
default:
$getid3->warning('Unexpected Bonk tag "'.$bonk_tag_name.'" at offset '.$getid3->info['bonk'][$bonk_tag_name]['offset']);
break;
}
}
public static function BonkIsValidTagName($possible_bonk_tag, $ignore_case=false) {
$ignore_case = $ignore_case ? 'i' : '';
return preg_match('/^(BONK|INFO| ID3|META)$/'.$ignore_case, $possible_bonk_tag);
}
} }

View file

@ -0,0 +1,73 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.dss.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)); // I thought time was in seconds, it's actually HHMMSS
$thisfile_dss['length'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2));
$thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1));
$thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100));
//$ThisFileInfo['audio']['bits_per_sample'] = ?;
//$ThisFileInfo['audio']['sample_rate'] = ?;
$ThisFileInfo['audio']['channels'] = 1;
$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);
}
}
?>

View file

@ -1,106 +1,98 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.dts.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing DTS Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // //
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.dts.php |
// | Module for analyzing DTS audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.dts.php,v 1.2 2006/11/16 13:14:26 ah Exp $
class getid3_dts
// 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/
class getid3_dts extends getid3_handler
{ {
public function Analyze() { 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/
$getid3 = $this->getid3; $ThisFileInfo['fileformat'] = 'dts';
$getid3->info['dts'] = array (); fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$info_dts = &$getid3->info['dts']; $DTSheader = fread($fd, 16);
$getid3->info['fileformat'] = 'dts'; $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4));
if ($ThisFileInfo['dts']['raw']['magic'] != 0x7FFE8001) {
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset '.$ThisFileInfo['avdataoffset'].', found "0x'.str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT).'"';
$header = fread($getid3->fp, 16); unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['dts']);
$fhBS = getid3_lib::BigEndian2Bin(substr($header, 4, 12)); return false;
$bs_offset = 0;
$info_dts['raw']['frame_type'] = bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['raw']['deficit_samples'] = bindec(substr($fhBS, $bs_offset, 5)); $bs_offset += 5;
$info_dts['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bs_offset, 7)); $bs_offset += 7;
$info_dts['raw']['frame_byte_size'] = bindec(substr($fhBS, $bs_offset, 14)); $bs_offset += 14;
$info_dts['raw']['channel_arrangement'] = bindec(substr($fhBS, $bs_offset, 6)); $bs_offset += 6;
$info_dts['raw']['sample_frequency'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4;
$info_dts['raw']['bitrate'] = bindec(substr($fhBS, $bs_offset, 5)); $bs_offset += 5;
$info_dts['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['raw']['extension_audio'] = bindec(substr($fhBS, $bs_offset, 3)); $bs_offset += 3;
$info_dts['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['raw']['lfe_effects'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2;
$info_dts['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
if ($info_dts['flags']['crc_present']) {
$info_dts['raw']['crc16'] = bindec(substr($fhBS, $bs_offset, 16)); $bs_offset += 16;
} }
$info_dts['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1;
$info_dts['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4; $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
$info_dts['raw']['copy_history'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2; $bsOffset = 0;
$info_dts['raw']['bits_per_sample'] = bindec(substr($fhBS, $bs_offset, 2)); $bs_offset += 2; $ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$info_dts['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5)); $bsOffset += 5;
$info_dts['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1)); $bsOffset += 1;
$info_dts['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1)); $bs_offset += 1; $ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7)); $bsOffset += 7;
$info_dts['raw']['dialog_normalization'] = bindec(substr($fhBS, $bs_offset, 4)); $bs_offset += 4; $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;
$info_dts['bitrate'] = $this->DTSbitrateLookup($info_dts['raw']['bitrate']); $ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
$info_dts['bits_per_sample'] = $this->DTSbitPerSampleLookup($info_dts['raw']['bits_per_sample']); $ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
$info_dts['sample_rate'] = $this->DTSsampleRateLookup($info_dts['raw']['sample_frequency']); $ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
$info_dts['dialog_normalization'] = $this->DTSdialogNormalization($info_dts['raw']['dialog_normalization'], $info_dts['raw']['encoder_soft_version']); $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
$info_dts['flags']['lossless'] = (($info_dts['raw']['bitrate'] == 31) ? true : false); $ThisFileInfo['dts']['flags']['lossless'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 31) ? true : false);
$info_dts['bitrate_mode'] = (($info_dts['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); $ThisFileInfo['dts']['bitrate_mode'] = (($ThisFileInfo['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
$info_dts['channels'] = $this->DTSnumChannelsLookup($info_dts['raw']['channel_arrangement']); $ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
$info_dts['channel_arrangement'] = $this->DTSchannelArrangementLookup($info_dts['raw']['channel_arrangement']); $ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
$getid3->info['audio']['dataformat'] = 'dts'; $ThisFileInfo['audio']['dataformat'] = 'dts';
$getid3->info['audio']['lossless'] = $info_dts['flags']['lossless']; $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless'];
$getid3->info['audio']['bitrate_mode'] = $info_dts['bitrate_mode']; $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode'];
$getid3->info['audio']['bits_per_sample'] = $info_dts['bits_per_sample']; $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample'];
$getid3->info['audio']['sample_rate'] = $info_dts['sample_rate']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate'];
$getid3->info['audio']['channels'] = $info_dts['channels']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels'];
$getid3->info['audio']['bitrate'] = $info_dts['bitrate']; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate'];
$getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / ($info_dts['bitrate'] / 8); if (isset($ThisFileInfo['avdataend'])) {
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
}
return true; return true;
} }
function DTSbitrateLookup($index) {
public static function DTSbitrateLookup($index) { $DTSbitrateLookup = array(
static $lookup = array (
0 => 32000, 0 => 32000,
1 => 56000, 1 => 56000,
2 => 64000, 2 => 64000,
@ -134,13 +126,11 @@ class getid3_dts extends getid3_handler
30 => 'variable', 30 => 'variable',
31 => 'lossless' 31 => 'lossless'
); );
return @$lookup[$index]; return @$DTSbitrateLookup[$index];
} }
function DTSsampleRateLookup($index) {
public static function DTSsampleRateLookup($index) { $DTSsampleRateLookup = array(
static $lookup = array (
0 => 'invalid', 0 => 'invalid',
1 => 8000, 1 => 8000,
2 => 16000, 2 => 16000,
@ -158,64 +148,59 @@ class getid3_dts extends getid3_handler
14 => 'invalid', 14 => 'invalid',
15 => 'invalid' 15 => 'invalid'
); );
return @$lookup[$index]; return @$DTSsampleRateLookup[$index];
} }
function DTSbitPerSampleLookup($index) {
public static function DTSbitPerSampleLookup($index) { $DTSbitPerSampleLookup = array(
static $lookup = array (
0 => 16, 0 => 16,
1 => 20, 1 => 20,
2 => 24, 2 => 24,
3 => 24, 3 => 24,
); );
return @$lookup[$index]; return @$DTSbitPerSampleLookup[$index];
} }
function DTSnumChannelsLookup($index) {
public static function DTSnumChannelsLookup($index) {
switch ($index) { switch ($index) {
case 0: case 0:
return 1; return 1;
break;
case 1: case 1:
case 2: case 2:
case 3: case 3:
case 4: case 4:
return 2; return 2;
break;
case 5: case 5:
case 6: case 6:
return 3; return 3;
break;
case 7: case 7:
case 8: case 8:
return 4; return 4;
break;
case 9: case 9:
return 5; return 5;
break;
case 10: case 10:
case 11: case 11:
case 12: case 12:
return 6; return 6;
break;
case 13: case 13:
return 7; return 7;
break;
case 14: case 14:
case 15: case 15:
return 8; return 8;
break;
} }
return false; return false;
} }
function DTSchannelArrangementLookup($index) {
public static function DTSchannelArrangementLookup($index) { $DTSchannelArrangementLookup = array(
static $lookup = array (
0 => 'A', 0 => 'A',
1 => 'A + B (dual mono)', 1 => 'A + B (dual mono)',
2 => 'L + R (stereo)', 2 => 'L + R (stereo)',
@ -233,18 +218,17 @@ class getid3_dts extends getid3_handler
14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2', 14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
15 => 'CL + C+ CR + L + R + SL + S + SR', 15 => 'CL + C+ CR + L + R + SL + S + SR',
); );
return (@$lookup[$index] ? @$lookup[$index] : 'user-defined'); return (@$DTSchannelArrangementLookup[$index] ? @$DTSchannelArrangementLookup[$index] : 'user-defined');
} }
function DTSdialogNormalization($index, $version) {
public static function DTSdialogNormalization($index, $version) {
switch ($version) { switch ($version) {
case 7: case 7:
return 0 - $index; return 0 - $index;
break;
case 6: case 6:
return 0 - 16 - $index; return 0 - 16 - $index;
break;
} }
return false; return false;
} }

View file

@ -0,0 +1,401 @@
<?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);
}
static 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': // 0x00
if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'PADDING': // 0x01
// ignore
break;
case 'APPLICATION': // 0x02
if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'SEEKTABLE': // 0x03
if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'VORBIS_COMMENT': // 0x04
$OldOffset = ftell($fd);
fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
fseek($fd, $OldOffset, SEEK_SET);
break;
case 'CUESHEET': // 0x05
if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
return false;
}
break;
case 'PICTURE': // 0x06
if (!getid3_flac::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;
}
static function FLACmetaBlockTypeLookup($blocktype) {
static $FLACmetaBlockTypeLookup = array();
if (empty($FLACmetaBlockTypeLookup)) {
$FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
$FLACmetaBlockTypeLookup[1] = 'PADDING';
$FLACmetaBlockTypeLookup[2] = 'APPLICATION';
$FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
$FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
$FLACmetaBlockTypeLookup[5] = 'CUESHEET';
$FLACmetaBlockTypeLookup[6] = 'PICTURE';
}
return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
}
static function FLACapplicationIDLookup($applicationid) {
static $FLACapplicationIDLookup = array();
if (empty($FLACapplicationIDLookup)) {
// http://flac.sourceforge.net/id.html
$FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol'
$FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
}
return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
}
static function FLACpictureTypeLookup($type_id) {
static $lookup = array (
0 => 'Other',
1 => '32x32 pixels \'file icon\' (PNG only)',
2 => 'Other file icon',
3 => 'Cover (front)',
4 => 'Cover (back)',
5 => 'Leaflet page',
6 => 'Media (e.g. label side of CD)',
7 => 'Lead artist/lead performer/soloist',
8 => 'Artist/performer',
9 => 'Conductor',
10 => 'Band/Orchestra',
11 => 'Composer',
12 => 'Lyricist/text writer',
13 => 'Recording Location',
14 => 'During recording',
15 => 'During performance',
16 => 'Movie/video screen capture',
17 => 'A bright coloured fish',
18 => 'Illustration',
19 => 'Band/artist logotype',
20 => 'Publisher/Studio logotype',
);
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
}
static 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'];
if ($ThisFileInfo['playtime_seconds'] > 0) {
$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;
}
static 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;
}
static 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;
}
static 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;
}
static function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) {
$picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1];
$offset = 0;
$picture['typeid'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
$picture['type'] = getid3_flac::FLACpictureTypeLookup($picture['typeid']);
$offset += 4;
$length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
$offset += 4;
$picture['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;
$picture['data_length'] = strlen($picture['image_data']);
unset($ThisFileInfo['flac']['PICTURE']['raw']);
return true;
}
}
?>

View file

@ -1,195 +1,226 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.la.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing LA audio files //
// +----------------------------------------------------------------------+ // dependencies: module.audio.riff.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.la.php |
// | Module for analyzing LA udio files |
// | dependencies: module.audio-video.riff.php |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.la.php,v 1.2 2006/11/02 10:48:01 ah Exp $
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_la
class getid3_la extends getid3_handler
{ {
public function Analyze() { function getid3_la(&$fd, &$ThisFileInfo) {
$offset = 0;
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
$getid3 = $this->getid3; switch (substr($rawdata, $offset, 4)) {
case 'LA02':
case 'LA03':
case 'LA04':
$ThisFileInfo['fileformat'] = 'la';
$ThisFileInfo['audio']['dataformat'] = 'la';
$ThisFileInfo['audio']['lossless'] = true;
$getid3->include_module('audio-video.riff'); $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;
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$raw_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE); $offset += 4;
if ($ThisFileInfo['la']['uncompressed_size'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero';
return false;
}
$getid3->info['fileformat'] = 'la'; $WAVEchunk = substr($rawdata, $offset, 4);
$getid3->info['audio']['dataformat'] = 'la'; if ($WAVEchunk !== 'WAVE') {
$getid3->info['audio']['lossless'] = true; $ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
return false;
}
$offset += 4;
$getid3->info['la']['version_major'] = (int)$raw_data{2}; $ThisFileInfo['la']['fmt_size'] = 24;
$getid3->info['la']['version_minor'] = (int)$raw_data{3}; if ($ThisFileInfo['la']['version'] >= 0.3) {
$getid3->info['la']['version'] = (float)$getid3->info['la']['version_major'] + ($getid3->info['la']['version_minor'] / 10);
$getid3->info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($raw_data, 4, 4)); $ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24;
$offset += 4;
$wave_chunk = substr($raw_data, 8, 4); } else {
if ($wave_chunk !== 'WAVE') {
throw new getid3_exception('Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset 8, found "'.$wave_chunk.'" ('.getid3_lib::PrintHexBytes($wave_chunk).') instead.');
}
$offset = 12; // version 0.2 didn't support additional data blocks
$ThisFileInfo['la']['header_size'] = 41;
$getid3->info['la']['fmt_size'] = 24; }
if ($getid3->info['la']['version'] >= 0.3) {
$getid3->info['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); $fmt_chunk = substr($rawdata, $offset, 4);
$getid3->info['la']['header_size'] = 49 + $getid3->info['la']['fmt_size'] - 24; if ($fmt_chunk !== 'fmt ') {
$offset += 4; $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;
} else { $ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$offset += 2;
// version 0.2 didn't support additional data blocks $ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
$getid3->info['la']['header_size'] = 41; $offset += 2;
} if ($ThisFileInfo['la']['channels'] == 0) {
$ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero';
return false;
}
$fmt_chunk = substr($raw_data, $offset, 4); $ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
if ($fmt_chunk !== 'fmt ') { $offset += 4;
throw new getid3_exception('Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'); if ($ThisFileInfo['la']['sample_rate'] == 0) {
} $ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero';
$offset += 4; return false;
}
$fmt_size = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); $ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$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;
$getid3->info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 2)); $ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 2; $offset += 4;
getid3_lib::ReadSequence('LittleEndian2Int', $getid3->info['la'], $raw_data, $offset, $ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
array ( $offset += 1;
'channels' => 2, $ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01);
'sample_rate' => 4, if ($ThisFileInfo['la']['version'] >= 0.4) {
'bytes_per_second' => 4, $ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02);
'bytes_per_sample' => 2, }
'bits_per_sample' => 2,
'samples' => 4
)
);
$offset += 18;
$getid3->info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int($raw_data{$offset++}); $ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
$getid3->info['la']['flags']['seekable'] = (bool)($getid3->info['la']['raw']['flags'] & 0x01); // mikeØbevin*de
if ($getid3->info['la']['version'] >= 0.4) { // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
$getid3->info['la']['flags']['high_compression'] = (bool)($getid3->info['la']['raw']['flags'] & 0x02); // 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;
}
$getid3->info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); $ThisFileInfo['la']['seekpoint_count'] = 0;
$offset += 4; if ($ThisFileInfo['la']['flags']['seekable']) {
$ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery']));
// mikeØbevin*de for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) {
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 $ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
// in earlier versions. A seekpoint is added every blocksize * seekevery $offset += 4;
// 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 ($getid3->info['la']['version'] >= 0.4) { if ($ThisFileInfo['la']['version'] >= 0.3) {
$getid3->info['la']['blocksize'] = 61440;
$getid3->info['la']['seekevery'] = 19;
} else {
$getid3->info['la']['blocksize'] = 73728;
$getid3->info['la']['seekevery'] = 16;
}
$getid3->info['la']['seekpoint_count'] = 0; // Following the main header information, the program outputs all of the
if ($getid3->info['la']['flags']['seekable']) { // seekpoints. Following these is what I called the 'footer start',
$getid3->info['la']['seekpoint_count'] = floor($getid3->info['la']['samples'] / ($getid3->info['la']['blocksize'] * $getid3->info['la']['seekevery'])); // i.e. the position immediately after the La audio data is finished.
$ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4;
for ($i = 0; $i < $getid3->info['la']['seekpoint_count']; $i++) { if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) {
$getid3->info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); $ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')';
$offset += 4; $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize'];
} }
}
if ($getid3->info['la']['version'] >= 0.3) { } else {
// Following the main header information, the program outputs all of the // La v0.2 didn't have FooterStart value
// seekpoints. Following these is what I called the 'footer start', $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend'];
// i.e. the position immediately after the La audio data is finished.
$getid3->info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($raw_data, $offset, 4)); }
$offset += 4;
if ($getid3->info['la']['footerstart'] > $getid3->info['filesize']) { if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
$getid3->warning('FooterStart value points to offset '.$getid3->info['la']['footerstart'].' which is beyond end-of-file ('.$getid3->info['filesize'].')'); if ($RIFFtempfilename = tempnam('*', 'id3')) {
$getid3->info['la']['footerstart'] = $getid3->info['filesize']; 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'];
} else { $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);
}
}
// La v0.2 didn't have FooterStart value // $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
$getid3->info['la']['footerstart'] = $getid3->info['avdataend']; $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;
}
if ($getid3->info['la']['footerstart'] < $getid3->info['avdataend']) { $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;
// Create riff header default:
$riff_data = 'WAVE'; if (substr($rawdata, $offset, 2) == 'LA') {
if ($getid3->info['la']['version'] == 0.2) { $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.';
$riff_data .= substr($raw_data, 12, 24); } else {
} else { $ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file';
$riff_data .= substr($raw_data, 16, 24); }
} return false;
if ($getid3->info['la']['footerstart'] < $getid3->info['avdataend']) { break;
fseek($getid3->fp, $getid3->info['la']['footerstart'], SEEK_SET); }
$riff_data .= fread($getid3->fp, $getid3->info['avdataend'] - $getid3->info['la']['footerstart']);
}
$riff_data = 'RIFF'.getid3_lib::LittleEndian2String(strlen($riff_data), 4, false).$riff_data;
// Clone getid3 - messing with offsets - better safe than sorry $ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels'];
$clone = clone $getid3; $ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate'];
$ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version'];
// Analyze clone by string return true;
$riff = new getid3_riff($clone); }
$riff->AnalyzeString($riff_data);
// Import from clone and destroy
$getid3->info['riff'] = $clone->info['riff'];
$getid3->warnings($clone->warnings());
unset($clone);
}
// $getid3->info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
$getid3->info['avdataend'] = $getid3->info['avdataoffset'] + $getid3->info['la']['footerstart'];
$getid3->info['avdataoffset'] = $getid3->info['avdataoffset'] + $offset;
$getid3->info['la']['compression_ratio'] = (float)(($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / $getid3->info['la']['uncompressed_size']);
$getid3->info['playtime_seconds'] = (float)($getid3->info['la']['samples'] / $getid3->info['la']['sample_rate']) / $getid3->info['la']['channels'];
$getid3->info['audio']['bitrate'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['playtime_seconds'];
$getid3->info['audio']['bits_per_sample'] = $getid3->info['la']['bits_per_sample'];
$getid3->info['audio']['channels'] = $getid3->info['la']['channels'];
$getid3->info['audio']['sample_rate'] = (int)$getid3->info['la']['sample_rate'];
$getid3->info['audio']['encoder'] = 'LA v'.$getid3->info['la']['version'];
return true;
}
} }

View file

@ -1,147 +1,124 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.lpac.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing LPAC Audio files //
// +----------------------------------------------------------------------+ // dependencies: module.audio-video.riff.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.lpac.php |
// | Module for analyzing LPAC Audio files |
// | dependencies: module.audio-video.riff.php |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.lpac.php,v 1.2 2006/11/02 10:48:01 ah Exp $
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_lpac
class getid3_lpac extends getid3_handler
{ {
public function Analyze() { function getid3_lpac(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; 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;
$getid3->include_module('audio-video.riff'); $ThisFileInfo['fileformat'] = 'lpac';
$ThisFileInfo['audio']['dataformat'] = 'lpac';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
// Magic bytes - 'LPAC' $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));
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
$lpac_header = fread($getid3->fp, 14); $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);
$getid3->info['avdataoffset'] += 14; if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) {
$ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set';
}
$getid3->info['lpac'] = array (); $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
$info_lpac = &$getid3->info['lpac']; $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);
$getid3->info['fileformat'] = 'lpac'; if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) {
$getid3->info['audio']['dataformat'] = 'lpac'; $ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"';
$getid3->info['audio']['lossless'] = true; }
$getid3->info['audio']['bitrate_mode'] = 'vbr'; 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;
$info_lpac['file_version'] = getid3_lib::BigEndian2Int($lpac_header{4}); default:
$flags['audio_type'] = getid3_lib::BigEndian2Int($lpac_header{5}); //$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org';
$info_lpac['total_samples'] = getid3_lib::BigEndian2Int(substr($lpac_header, 6, 4)); break;
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($lpac_header, 10, 4)); }
$info_lpac['flags']['is_wave'] = (bool)($flags['audio_type'] & 0x40); $dummy = $ThisFileInfo;
$info_lpac['flags']['stereo'] = (bool)($flags['audio_type'] & 0x04); $riff = new getid3_riff($fd, $dummy);
$info_lpac['flags']['24_bit'] = (bool)($flags['audio_type'] & 0x02); unset($riff);
$info_lpac['flags']['16_bit'] = (bool)($flags['audio_type'] & 0x01); $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'];
if ($info_lpac['flags']['24_bit'] && $info_lpac['flags']['16_bit']) { $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
$getid3->warning('24-bit and 16-bit flags cannot both be set');
}
$info_lpac['flags']['fast_compress'] = (bool)($flags['parameters'] & 0x40000000); if ($ThisFileInfo['lpac']['flags']['24_bit']) {
$info_lpac['flags']['random_access'] = (bool)($flags['parameters'] & 0x08000000); $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
$info_lpac['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) {
$info_lpac['flags']['adaptive_prediction_order'] = (bool)($flags['parameters'] & 0x00800000); $ThisFileInfo['audio']['bits_per_sample'] = 16;
$info_lpac['flags']['adaptive_quantization'] = (bool)($flags['parameters'] & 0x00400000); } else {
$info_lpac['flags']['joint_stereo'] = (bool)($flags['parameters'] & 0x00040000); $ThisFileInfo['audio']['bits_per_sample'] = 8;
$info_lpac['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; }
$info_lpac['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
if ($info_lpac['flags']['fast_compress'] && ($info_lpac['max_prediction_order'] != 3)) { if ($ThisFileInfo['lpac']['flags']['fast_compress']) {
$getid3->warning('max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info_lpac['max_prediction_order'].'"'); // 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;
}
}
switch ($info_lpac['file_version']) { $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
case 6: return true;
if ($info_lpac['flags']['adaptive_quantization']) { }
$getid3->warning('adaptive_quantization expected to be false in LPAC file stucture v6, actually true');
}
if ($info_lpac['quantization'] != 20) {
$getid3->warning('Quantization expected to be 20 in LPAC file stucture v6, actually '.$info_lpac['flags']['Q']);
}
break;
default:
//$getid3->warning('This version of getID3() only supports LPAC file format version 6, this file is version '.$info_lpac['file_version'].' - please report to info@getid3.org');
break;
}
// Clone getid3 - messing with something - better safe than sorry
$clone = clone $getid3;
// Analyze clone by fp
$riff = new getid3_riff($clone);
$riff->Analyze();
// Import from clone and destroy
$getid3->info['avdataoffset'] = $clone->info['avdataoffset'];
$getid3->info['riff'] = $clone->info['riff'];
//$info_lpac['comments']['comment'] = $clone->info['comments'];
$getid3->info['audio']['sample_rate'] = $clone->info['audio']['sample_rate'];
$getid3->warnings($clone->warnings());
unset($clone);
$getid3->info['audio']['channels'] = ($info_lpac['flags']['stereo'] ? 2 : 1);
if ($info_lpac['flags']['24_bit']) {
$getid3->info['audio']['bits_per_sample'] = $getid3->info['riff']['audio'][0]['bits_per_sample'];
} elseif ($info_lpac['flags']['16_bit']) {
$getid3->info['audio']['bits_per_sample'] = 16;
} else {
$getid3->info['audio']['bits_per_sample'] = 8;
}
if ($info_lpac['flags']['fast_compress']) {
// fast
$getid3->info['audio']['encoder_options'] = '-1';
} else {
switch ($info_lpac['max_prediction_order']) {
case 20: // simple
$getid3->info['audio']['encoder_options'] = '-2';
break;
case 30: // medium
$getid3->info['audio']['encoder_options'] = '-3';
break;
case 40: // high
$getid3->info['audio']['encoder_options'] = '-4';
break;
case 60: // extrahigh
$getid3->info['audio']['encoder_options'] = '-5';
break;
}
}
$getid3->info['playtime_seconds'] = $info_lpac['total_samples'] / $getid3->info['audio']['sample_rate'];
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
return true;
}
} }

File diff suppressed because it is too large Load diff

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 (!preg_match('#^(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 (!preg_match('#^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 (!preg_match('#^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 (!preg_match('#^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;
}
}
?>

View file

@ -1,216 +1,201 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.monkey.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Monkey's Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.monkey.php |
// | Module for analyzing Monkey's Audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.monkey.php,v 1.2 2006/11/02 10:48:01 ah Exp $
class getid3_monkey
class getid3_monkey extends getid3_handler
{ {
public function Analyze() { 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
$getid3 = $this->getid3; $ThisFileInfo['fileformat'] = 'mac';
$ThisFileInfo['audio']['dataformat'] = 'mac';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$ThisFileInfo['audio']['lossless'] = true;
// based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de> $ThisFileInfo['monkeys_audio']['raw'] = array();
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html $thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio'];
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
$getid3->info['fileformat'] = 'mac'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->info['audio']['dataformat'] = 'mac'; $MACheaderData = fread($fd, 74);
$getid3->info['audio']['bitrate_mode'] = 'vbr';
$getid3->info['audio']['lossless'] = true;
$getid3->info['monkeys_audio']['raw'] = array (); $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
$info_monkeys_audio = &$getid3->info['monkeys_audio']; if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') {
$info_monkeys_audio_raw = &$info_monkeys_audio['raw']; $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+
// Read file header if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
$mac_header_data = fread($getid3->fp, 74); $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;
$info_monkeys_audio_raw['magic'] = 'MAC '; // Magic bytes // 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;
}
// Read MAC version $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
$info_monkeys_audio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($mac_header_data, 4, 2)); // appears to be uint32 in 3.98+ $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'];
// Parse MAC Header < v3980 // add size of MAC header to avdataoffset
if ($info_monkeys_audio_raw['nVersion'] < 3980) { 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'];
getid3_lib::ReadSequence("LittleEndian2Int", $info_monkeys_audio_raw, $mac_header_data, 6, $ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
array ( } else {
'nCompressionLevel' => 2, $ThisFileInfo['avdataoffset'] += $offset;
'nFormatFlags' => 2, }
'nChannels' => 2,
'nSampleRate' => 4,
'nHeaderDataBytes' => 4,
'nWAVTerminatingBytes' => 4,
'nTotalFrames' => 4,
'nFinalFrameSamples' => 4,
'nPeakLevel' => 4,
'IGNORE-1' => 2,
'nSeekElements' => 2
)
);
}
// Parse MAC Header >= v3980 if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
else { if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
//$ThisFileInfo['warning'][] = 'cFileMD5 is null';
getid3_lib::ReadSequence("LittleEndian2Int", $info_monkeys_audio_raw, $mac_header_data, 8, } else {
array ( $ThisFileInfo['md5_data_source'] = '';
// APE_DESCRIPTOR $md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
'nDescriptorBytes' => 4, for ($i = 0; $i < strlen($md5); $i++) {
'nHeaderBytes' => 4, $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
'nSeekTableBytes' => 4, }
'nHeaderDataBytes' => 4, if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
'nAPEFrameDataBytes' => 4, unset($ThisFileInfo['md5_data_source']);
'nAPEFrameDataBytesHigh'=> 4, }
'nTerminatingDataBytes' => 4, }
}
// MD5 - string
'cFileMD5' => -16,
// APE_HEADER
'nCompressionLevel' => 2,
'nFormatFlags' => 2,
'nBlocksPerFrame' => 4,
'nFinalFrameBlocks' => 4,
'nTotalFrames' => 4,
'nBitsPerSample' => 2,
'nChannels' => 2,
'nSampleRate' => 4
)
);
}
// Process data
$info_monkeys_audio['flags']['8-bit'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0001);
$info_monkeys_audio['flags']['crc-32'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0002);
$info_monkeys_audio['flags']['peak_level'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0004);
$info_monkeys_audio['flags']['24-bit'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0008);
$info_monkeys_audio['flags']['seek_elements'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0010);
$info_monkeys_audio['flags']['no_wav_header'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0020);
$info_monkeys_audio['version'] = $info_monkeys_audio_raw['nVersion'] / 1000;
$info_monkeys_audio['compression'] = getid3_monkey::MonkeyCompressionLevelNameLookup($info_monkeys_audio_raw['nCompressionLevel']);
$info_monkeys_audio['bits_per_sample'] = ($info_monkeys_audio['flags']['24-bit'] ? 24 : ($info_monkeys_audio['flags']['8-bit'] ? 8 : 16));
$info_monkeys_audio['channels'] = $info_monkeys_audio_raw['nChannels'];
$getid3->info['audio']['channels'] = $info_monkeys_audio['channels'];
$info_monkeys_audio['sample_rate'] = $info_monkeys_audio_raw['nSampleRate'];
$getid3->info['audio']['sample_rate'] = $info_monkeys_audio['sample_rate'];
if ($info_monkeys_audio['flags']['peak_level']) {
$info_monkeys_audio['peak_level'] = $info_monkeys_audio_raw['nPeakLevel'];
$info_monkeys_audio['peak_ratio'] = $info_monkeys_audio['peak_level'] / pow(2, $info_monkeys_audio['bits_per_sample'] - 1);
}
// MAC >= v3980
if ($info_monkeys_audio_raw['nVersion'] >= 3980) {
$info_monkeys_audio['samples'] = (($info_monkeys_audio_raw['nTotalFrames'] - 1) * $info_monkeys_audio_raw['nBlocksPerFrame']) + $info_monkeys_audio_raw['nFinalFrameBlocks'];
}
// MAC < v3980
else {
$info_monkeys_audio['samples_per_frame'] = getid3_monkey::MonkeySamplesPerFrame($info_monkeys_audio_raw['nVersion'], $info_monkeys_audio_raw['nCompressionLevel']);
$info_monkeys_audio['samples'] = (($info_monkeys_audio_raw['nTotalFrames'] - 1) * $info_monkeys_audio['samples_per_frame']) + $info_monkeys_audio_raw['nFinalFrameSamples'];
}
$info_monkeys_audio['playtime'] = $info_monkeys_audio['samples'] / $info_monkeys_audio['sample_rate'];
$getid3->info['playtime_seconds'] = $info_monkeys_audio['playtime'];
$info_monkeys_audio['compressed_size'] = $getid3->info['avdataend'] - $getid3->info['avdataoffset'];
$info_monkeys_audio['uncompressed_size'] = $info_monkeys_audio['samples'] * $info_monkeys_audio['channels'] * ($info_monkeys_audio['bits_per_sample'] / 8);
$info_monkeys_audio['compression_ratio'] = $info_monkeys_audio['compressed_size'] / ($info_monkeys_audio['uncompressed_size'] + $info_monkeys_audio_raw['nHeaderDataBytes']);
$info_monkeys_audio['bitrate'] = (($info_monkeys_audio['samples'] * $info_monkeys_audio['channels'] * $info_monkeys_audio['bits_per_sample']) / $info_monkeys_audio['playtime']) * $info_monkeys_audio['compression_ratio'];
$getid3->info['audio']['bitrate'] = $info_monkeys_audio['bitrate'];
$getid3->info['audio']['bits_per_sample'] = $info_monkeys_audio['bits_per_sample'];
$getid3->info['audio']['encoder'] = 'MAC v'.number_format($info_monkeys_audio['version'], 2);
$getid3->info['audio']['encoder_options'] = ucfirst($info_monkeys_audio['compression']).' compression';
// MAC >= v3980 - get avdataoffsets from MAC header
if ($info_monkeys_audio_raw['nVersion'] >= 3980) {
$getid3->info['avdataoffset'] += $info_monkeys_audio_raw['nDescriptorBytes'] + $info_monkeys_audio_raw['nHeaderBytes'] + $info_monkeys_audio_raw['nSeekTableBytes'] + $info_monkeys_audio_raw['nHeaderDataBytes'];
$getid3->info['avdataend'] -= $info_monkeys_audio_raw['nTerminatingDataBytes'];
}
// MAC < v3980 Add size of MAC header to avdataoffset
else {
$getid3->info['avdataoffset'] += 8;
}
// Convert md5sum to 32 byte string
if (@$info_monkeys_audio_raw['cFileMD5']) {
if ($info_monkeys_audio_raw['cFileMD5'] !== str_repeat("\x00", 16)) {
$getid3->info['md5_data_source'] = '';
$md5 = $info_monkeys_audio_raw['cFileMD5'];
for ($i = 0; $i < strlen($md5); $i++) {
$getid3->info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
}
if (!preg_match('/^[0-9a-f]{32}$/', $getid3->info['md5_data_source'])) {
unset($getid3->info['md5_data_source']);
}
}
}
return true;
}
public static function MonkeyCompressionLevelNameLookup($compression_level) { $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';
static $lookup = array ( return true;
0 => 'unknown', }
1000 => 'fast',
2000 => 'normal',
3000 => 'high',
4000 => 'extra-high',
5000 => 'insane'
);
return (isset($lookup[$compression_level]) ? $lookup[$compression_level] : 'invalid');
}
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) {
public static function MonkeySamplesPerFrame($version_id, $compression_level) { if ($versionid >= 3950) {
return 73728 * 4;
if ($version_id >= 3950) { } elseif ($versionid >= 3900) {
return 73728 * 4; return 73728;
} } elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
if (($version_id >= 3900) || (($version_id >= 3800) && ($compression_level == 4000))) { return 73728;
return 73728; } else {
} return 9216;
return 9216; }
} }
} }

File diff suppressed because it is too large Load diff

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 (preg_match('#^MPCK#', $ThisFileInfo['mpc']['header']['preamble'])) {
// this is SV8
return $this->ParseMPCsv8($fd, $ThisFileInfo);
} elseif (preg_match('#^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);
}
}
?>

View file

@ -0,0 +1,565 @@
<?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;
}
static 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;
}
static 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;
}
static 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['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
break 2;
}
$VorbisCommentPage++;
$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
// 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']);
if (!isset($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage])) {
$ThisFileInfo['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd);
break;
}
$readlength = getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1);
if ($readlength <= 0) {
$ThisFileInfo['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd);
break;
}
$commentdata .= fread($fd, $readlength);
//$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;
}
static function SpeexBandModeLookup($mode) {
static $SpeexBandModeLookup = array();
if (empty($SpeexBandModeLookup)) {
$SpeexBandModeLookup[0] = 'narrow';
$SpeexBandModeLookup[1] = 'wide';
$SpeexBandModeLookup[2] = 'ultra-wide';
}
return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
}
static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
for ($i = 0; $i < $SegmentNumber; $i++) {
$segmentlength = 0;
foreach ($OggInfoArray['segment_table'] as $key => $value) {
$segmentlength += $value;
if ($value < 255) {
break;
}
}
}
return $segmentlength;
}
static function get_quality_from_nominal_bitrate($nominal_bitrate) {
// decrease precision
$nominal_bitrate = $nominal_bitrate / 1000;
if ($nominal_bitrate < 128) {
// q-1 to q4
$qval = ($nominal_bitrate - 64) / 16;
} elseif ($nominal_bitrate < 256) {
// q4 to q8
$qval = $nominal_bitrate / 32;
} elseif ($nominal_bitrate < 320) {
// q8 to q9
$qval = ($nominal_bitrate + 256) / 64;
} else {
// q9 to q10
$qval = ($nominal_bitrate + 1300) / 180;
}
//return $qval; // 5.031324
//return intval($qval); // 5
return round($qval, 1); // 5 or 4.9
}
}
?>

View file

@ -1,467 +1,406 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.optimfrog.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing OptimFROG audio files //
// +----------------------------------------------------------------------+ // dependencies: module.audio.riff.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.optimfrog.php |
// | Module for analyzing OptimFROG Audio files |
// | dependencies: module.audio-video.riff.php |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.optimfrog.php,v 1.3 2006/11/02 10:48:01 ah Exp $
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_optimfrog
class getid3_optimfrog extends getid3_handler
{ {
public function Analyze() { function getid3_optimfrog(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'ofr';
$getid3 = $this->getid3; $ThisFileInfo['audio']['dataformat'] = 'ofr';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$getid3->include_module('audio-video.riff'); $ThisFileInfo['audio']['lossless'] = true;
$getid3->info['audio']['dataformat'] = 'ofr'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->info['audio']['bitrate_mode'] = 'vbr'; $OFRheader = fread($fd, 8);
$getid3->info['audio']['lossless'] = true; if (substr($OFRheader, 0, 5) == '*RIFF') {
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
$ofr_header = fread($getid3->fp, 8);
} elseif (substr($OFRheader, 0, 3) == 'OFR') {
if (substr($ofr_header, 0, 5) == '*RIFF') {
return $this->ParseOptimFROGheader42($getid3->fp, $getid3->info); return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
} elseif (substr($ofr_header, 0, 3) == 'OFR') { }
return $this->ParseOptimFROGheader45($getid3->fp, $getid3->info);
} $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
unset($ThisFileInfo['fileformat']);
throw new getid3_exception('Expecting "*RIFF" or "OFR " at offset '.$getid3->info['avdataoffset'].', found "'.$ofr_header.'"'); return false;
} }
function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
private function ParseOptimFROGheader42() { // for fileformat of v4.21 and older
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$OptimFROGheaderData = fread($fd, 45);
// for fileformat of v4.21 and older $ThisFileInfo['avdataoffset'] = 45;
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
$ofr_header_data = fread($getid3->fp, 45); $OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
$getid3->info['avdataoffset'] = 45; $RIFFdata = substr($OptimFROGheaderData, 1, 44);
$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
$ofr_encoder_version_raw = getid3_lib::LittleEndian2Int(substr($ofr_header_data, 0, 1)); $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
$ofr_encoder_version_major = floor($ofr_encoder_version_raw / 10);
$ofr_encoder_version_minor = $ofr_encoder_version_raw - ($ofr_encoder_version_major * 10); if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
$riff_data = substr($ofr_header_data, 1, 44); $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
$origna_riff_header_size = getid3_lib::LittleEndian2Int(substr($riff_data, 4, 4)) + 8; fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
$origna_riff_data_size = getid3_lib::LittleEndian2Int(substr($riff_data, 40, 4)) + 44; $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
}
if ($origna_riff_header_size > $origna_riff_data_size) {
$getid3->info['avdataend'] -= ($origna_riff_header_size - $origna_riff_data_size); // move the data chunk after all other chunks (if any)
fseek($getid3->fp, $getid3->info['avdataend'], SEEK_SET); // so that the RIFF parser doesn't see EOF when trying
$riff_data .= fread($getid3->fp, $origna_riff_header_size - $origna_riff_data_size); // to skip over the data chunk
} $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
// move the data chunk after all other chunks (if any)
// so that the RIFF parser doesn't see EOF when trying $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
// to skip over the data chunk $ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels'];
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate'];
$riff_data = substr($riff_data, 0, 36).substr($riff_data, 44).substr($riff_data, 36, 8); $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));
// Save audio info key $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
$saved_info_audio = $getid3->info['audio'];
return true;
// Instantiate riff module and analyze string }
$riff = new getid3_riff($getid3);
$riff->AnalyzeString($riff_data);
function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
// Restore info key // for fileformat of v4.50a and higher
$getid3->info['audio'] = $saved_info_audio;
$RIFFdata = '';
$getid3->info['audio']['encoder'] = 'OptimFROG '.$ofr_encoder_version_major.'.'.$ofr_encoder_version_minor; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->info['audio']['channels'] = $getid3->info['riff']['audio'][0]['channels']; while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
$getid3->info['audio']['sample_rate'] = $getid3->info['riff']['audio'][0]['sample_rate']; $BlockOffset = ftell($fd);
$getid3->info['audio']['bits_per_sample'] = $getid3->info['riff']['audio'][0]['bits_per_sample']; $BlockData = fread($fd, 8);
$getid3->info['playtime_seconds'] = $origna_riff_data_size / ($getid3->info['audio']['channels'] * $getid3->info['audio']['sample_rate'] * ($getid3->info['audio']['bits_per_sample'] / 8)); $offset = 8;
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds']; $BlockName = substr($BlockData, 0, 4);
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
$getid3->info['fileformat'] = 'ofr';
if ($BlockName == 'OFRX') {
return true; $BlockName = 'OFR ';
} }
if (!isset($ThisFileInfo['ofr'][$BlockName])) {
$ThisFileInfo['ofr'][$BlockName] = array();
}
private function ParseOptimFROGheader45() { $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
$getid3 = $this->getid3; switch ($BlockName) {
case 'OFR ':
// for fileformat of v4.50a and higher
// shortcut
$riff_data = ''; $thisfile_ofr_thisblock['offset'] = $BlockOffset;
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $thisfile_ofr_thisblock['size'] = $BlockSize;
while (!feof($getid3->fp) && (ftell($getid3->fp) < $getid3->info['avdataend'])) { $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
switch ($BlockSize) {
$block_offset = ftell($getid3->fp); case 12:
$block_data = fread($getid3->fp, 8); case 15:
$offset = 8; // good
$block_name = substr($block_data, 0, 4); break;
$block_size = getid3_lib::LittleEndian2Int(substr($block_data, 4, 4));
default:
if ($block_name == 'OFRX') { $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
$block_name = 'OFR '; break;
} }
if (!isset($getid3->info['ofr'][$block_name])) { $BlockData .= fread($fd, $BlockSize);
$getid3->info['ofr'][$block_name] = array ();
} $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
$info_ofr_this_block = &$getid3->info['ofr'][$block_name]; $offset += 6;
$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
switch ($block_name) { $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
case 'OFR ': $offset += 1;
$thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
// shortcut $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
$info_ofr_this_block['offset'] = $block_offset; $offset += 1;
$info_ofr_this_block['size'] = $block_size; $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
$offset += 4;
$getid3->info['audio']['encoder'] = 'OptimFROG 4.50 alpha';
switch ($block_size) { if ($BlockSize > 12) {
case 12:
case 15: // OFR 4.504b or higher
// good $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
break; $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']);
default: $offset += 2;
$getid3->warning('"'.$block_name.'" contains more data than expected (expected 12 or 15 bytes, found '.$block_size.' bytes)'); $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
break; $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
} $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
$block_data .= fread($getid3->fp, $block_size); $offset += 1;
$info_ofr_this_block['total_samples'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 6)); $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
$offset += 6; $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
$info_ofr_this_block['raw']['sample_type'] = getid3_lib::LittleEndian2Int($block_data{$offset++}); if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
$info_ofr_this_block['sample_type'] = $this->OptimFROGsampleTypeLookup($info_ofr_this_block['raw']['sample_type']); 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
$info_ofr_this_block['channel_config'] = getid3_lib::LittleEndian2Int($block_data{$offset++}); // between lossless and lossy other than the file extension.
$info_ofr_this_block['channels'] = $info_ofr_this_block['channel_config']; $ThisFileInfo['audio']['dataformat'] = 'ofs';
$ThisFileInfo['audio']['lossless'] = true;
$info_ofr_this_block['sample_rate'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4)); }
$offset += 4; }
if ($block_size > 12) { }
// OFR 4.504b or higher $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
$info_ofr_this_block['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($info_ofr_this_block['channel_config']); $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
$info_ofr_this_block['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2)); $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
$info_ofr_this_block['encoder'] = $this->OptimFROGencoderNameLookup($info_ofr_this_block['raw']['encoder_id']); break;
$offset += 2;
$info_ofr_this_block['raw']['compression'] = getid3_lib::LittleEndian2Int($block_data{$offset++}); case 'COMP':
$info_ofr_this_block['compression'] = $this->OptimFROGcompressionLookup($info_ofr_this_block['raw']['compression']); // unlike other block types, there CAN be multiple COMP blocks
$info_ofr_this_block['speedup'] = $this->OptimFROGspeedupLookup($info_ofr_this_block['raw']['compression']);
$COMPdata['offset'] = $BlockOffset;
$getid3->info['audio']['encoder'] = 'OptimFROG '.$info_ofr_this_block['encoder']; $COMPdata['size'] = $BlockSize;
$getid3->info['audio']['encoder_options'] = '--mode '.$info_ofr_this_block['compression'];
if ($ThisFileInfo['avdataoffset'] == 0) {
if ((($info_ofr_this_block['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507 $ThisFileInfo['avdataoffset'] = $BlockOffset;
if (preg_match('/\.ofs$/i', $getid3->filename)) { }
// 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. // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
$getid3->info['audio']['dataformat'] = 'ofs'; $BlockData .= fread($fd, 14);
$getid3->info['audio']['lossless'] = true; 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));
$getid3->info['audio']['channels'] = $info_ofr_this_block['channels']; $offset += 4;
$getid3->info['audio']['sample_rate'] = $info_ofr_this_block['sample_rate']; $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$getid3->info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($info_ofr_this_block['raw']['sample_type']); $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
break; $offset += 1;
$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
$COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
case 'COMP': $offset += 1;
// unlike other block types, there CAN be multiple COMP blocks $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
//$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
$comp_data['offset'] = $block_offset; $offset += 2;
$comp_data['size'] = $block_size;
if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
if ($getid3->info['avdataoffset'] == 0) {
$getid3->info['avdataoffset'] = $block_offset; // OFR 4.504b or higher
} $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
$COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data $offset += 2;
$block_data .= fread($getid3->fp, 14);
fseek($getid3->fp, $block_size - 14, SEEK_CUR); }
$comp_data['crc_32'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4)); if ($COMPdata['crc_32'] == 0x454E4F4E) {
$offset += 4; // ASCII value of 'NONE' - placeholder value in v4.50a
$COMPdata['crc_32'] = false;
$comp_data['sample_count'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4)); }
$offset += 4;
$thisfile_ofr_thisblock[] = $COMPdata;
$comp_data['raw']['sample_type'] = getid3_lib::LittleEndian2Int($block_data{$offset++}); break;
$comp_data['sample_type'] = $this->OptimFROGsampleTypeLookup($comp_data['raw']['sample_type']);
case 'HEAD':
$comp_data['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int($block_data{$offset++}); $thisfile_ofr_thisblock['offset'] = $BlockOffset;
$comp_data['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($comp_data['raw']['channel_configuration']); $thisfile_ofr_thisblock['size'] = $BlockSize;
$comp_data['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2)); $RIFFdata .= fread($fd, $BlockSize);
$offset += 2; break;
if ($getid3->info['ofr']['OFR ']['size'] > 12) { case 'TAIL':
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
// OFR 4.504b or higher $thisfile_ofr_thisblock['size'] = $BlockSize;
$comp_data['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2));
$comp_data['encoder'] = $this->OptimFROGencoderNameLookup($comp_data['raw']['encoder_id']); if ($BlockSize > 0) {
$offset += 2; $RIFFdata .= fread($fd, $BlockSize);
} }
break;
if ($comp_data['crc_32'] == 0x454E4F4E) {
// ASCII value of 'NONE' - placeholder value in v4.50a case 'RECV':
$comp_data['crc_32'] = false; // block contains no useful meta data - simply note and skip
}
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
$info_ofr_this_block[] = $comp_data; $thisfile_ofr_thisblock['size'] = $BlockSize;
break;
fseek($fd, $BlockSize, SEEK_CUR);
case 'HEAD': break;
$info_ofr_this_block['offset'] = $block_offset;
$info_ofr_this_block['size'] = $block_size;
case 'APET':
$riff_data .= fread($getid3->fp, $block_size); // APEtag v2
break;
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
case 'TAIL': $thisfile_ofr_thisblock['size'] = $BlockSize;
$info_ofr_this_block['offset'] = $block_offset; $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()';
$info_ofr_this_block['size'] = $block_size;
fseek($fd, $BlockSize, SEEK_CUR);
if ($block_size > 0) { break;
$riff_data .= fread($getid3->fp, $block_size);
}
break; case 'MD5 ':
// APEtag v2
case 'RECV':
// block contains no useful meta data - simply note and skip $thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize;
$info_ofr_this_block['offset'] = $block_offset;
$info_ofr_this_block['size'] = $block_size; if ($BlockSize == 16) {
fseek($getid3->fp, $block_size, SEEK_CUR); $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
break; $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'];
case 'APET': } else {
// APEtag v2
$ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
$info_ofr_this_block['offset'] = $block_offset; fseek($fd, $BlockSize, SEEK_CUR);
$info_ofr_this_block['size'] = $block_size;
$getid3->warning('APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()'); }
break;
fseek($getid3->fp, $block_size, SEEK_CUR);
break;
default:
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
case 'MD5 ': $thisfile_ofr_thisblock['size'] = $BlockSize;
// APEtag v2
$ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
$info_ofr_this_block['offset'] = $block_offset; fseek($fd, $BlockSize, SEEK_CUR);
$info_ofr_this_block['size'] = $block_size; break;
}
if ($block_size == 16) { }
if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
$info_ofr_this_block['md5_binary'] = fread($getid3->fp, $block_size); $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
$info_ofr_this_block['md5_string'] = getid3_lib::PrintHexBytes($info_ofr_this_block['md5_binary'], true, false, false); }
$getid3->info['md5_data_source'] = $info_ofr_this_block['md5_string'];
$ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
} else { $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
$getid3->warning('Expecting block size of 16 in "MD5 " chunk, found '.$block_size.' instead'); // move the data chunk after all other chunks (if any)
fseek($getid3->fp, $block_size, SEEK_CUR); // 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);
break; getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
return true;
default: }
$info_ofr_this_block['offset'] = $block_offset;
$info_ofr_this_block['size'] = $block_size;
function OptimFROGsampleTypeLookup($SampleType) {
$getid3->warning('Unhandled OptimFROG block type "'.$block_name.'" at offset '.$info_ofr_this_block['offset']); static $OptimFROGsampleTypeLookup = array(
fseek($getid3->fp, $block_size, SEEK_CUR); 0 => 'unsigned int (8-bit)',
break; 1 => 'signed int (8-bit)',
} 2 => 'unsigned int (16-bit)',
} 3 => 'signed int (16-bit)',
4 => 'unsigned int (24-bit)',
if (isset($getid3->info['ofr']['TAIL']['offset'])) { 5 => 'signed int (24-bit)',
$getid3->info['avdataend'] = $getid3->info['ofr']['TAIL']['offset']; 6 => 'unsigned int (32-bit)',
} 7 => 'signed int (32-bit)',
8 => 'float 0.24 (32-bit)',
$getid3->info['playtime_seconds'] = (float)$getid3->info['ofr']['OFR ']['total_samples'] / ($getid3->info['audio']['channels'] * $getid3->info['audio']['sample_rate']); 9 => 'float 16.8 (32-bit)',
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds']; 10 => 'float 24.0 (32-bit)'
);
// move the data chunk after all other chunks (if any) return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
// so that the RIFF parser doesn't see EOF when trying }
// to skip over the data chunk
function OptimFROGbitsPerSampleTypeLookup($SampleType) {
$riff_data = substr($riff_data, 0, 36).substr($riff_data, 44).substr($riff_data, 36, 8); static $OptimFROGbitsPerSampleTypeLookup = array(
0 => 8,
// Save audio info key 1 => 8,
$saved_info_audio = $getid3->info['audio']; 2 => 16,
3 => 16,
// Instantiate riff module and analyze string 4 => 24,
$riff = new getid3_riff($getid3); 5 => 24,
$riff->AnalyzeString($riff_data); 6 => 32,
7 => 32,
// Restore info key 8 => 32,
$getid3->info['audio'] = $saved_info_audio; 9 => 32,
10 => 32
$getid3->info['fileformat'] = 'ofr'; );
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
return true; }
}
function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
static $OptimFROGchannelConfigurationLookup = array(
0 => 'mono',
public static function OptimFROGsampleTypeLookup($sample_type) { 1 => 'stereo'
);
static $lookup = array ( return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
0 => 'unsigned int (8-bit)', }
1 => 'signed int (8-bit)',
2 => 'unsigned int (16-bit)', function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
3 => 'signed int (16-bit)', static $OptimFROGchannelConfigNumChannelsLookup = array(
4 => 'unsigned int (24-bit)', 0 => 1,
5 => 'signed int (24-bit)', 1 => 2
6 => 'unsigned int (32-bit)', );
7 => 'signed int (32-bit)', return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
8 => 'float 0.24 (32-bit)', }
9 => 'float 16.8 (32-bit)',
10 => 'float 24.0 (32-bit)'
);
// function OptimFROGalgorithmNameLookup($AlgorithID) {
return @$lookup[$sample_type]; // static $OptimFROGalgorithmNameLookup = array();
} // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
// }
public static function OptimFROGbitsPerSampleTypeLookup($sample_type) { function OptimFROGencoderNameLookup($EncoderID) {
// version = (encoderID >> 4) + 4500
static $lookup = array ( // system = encoderID & 0xF
0 => 8,
1 => 8, $EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
2 => 16, $EncoderSystemID = ($EncoderID & 0x0F);
3 => 16,
4 => 24, static $OptimFROGencoderSystemLookup = array(
5 => 24, 0x00 => 'Windows console',
6 => 32, 0x01 => 'Linux console',
7 => 32, 0x0F => 'unknown'
8 => 32, );
9 => 32, return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
10 => 32 }
);
function OptimFROGcompressionLookup($CompressionID) {
return @$lookup[$sample_type]; // mode = compression >> 3
} // speedup = compression & 0x07
$CompressionModeID = ($CompressionID & 0xF8) >> 3;
//$CompressionSpeedupID = ($CompressionID & 0x07);
public static function OptimFROGchannelConfigurationLookup($channel_configuration) {
static $OptimFROGencoderModeLookup = array(
static $lookup = array ( 0x00 => 'fast',
0 => 'mono', 0x01 => 'normal',
1 => 'stereo' 0x02 => 'high',
); 0x03 => 'extra', // extranew (some versions)
0x04 => 'best', // bestnew (some versions)
return @$lookup[$channel_configuration]; 0x05 => 'ultra',
} 0x06 => 'insane',
0x07 => 'highnew',
0x08 => 'extranew',
0x09 => 'bestnew'
public static function OptimFROGchannelConfigNumChannelsLookup($channel_configuration) { );
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
static $lookup = array ( }
0 => 1,
1 => 2 function OptimFROGspeedupLookup($CompressionID) {
); // mode = compression >> 3
// speedup = compression & 0x07
return @$lookup[$channel_configuration];
} //$CompressionModeID = ($CompressionID & 0xF8) >> 3;
$CompressionSpeedupID = ($CompressionID & 0x07);
static $OptimFROGencoderSpeedupLookup = array(
public static function OptimFROGencoderNameLookup($encoder_id) { 0x00 => '1x',
0x01 => '2x',
// version = (encoderID >> 4) + 4500 0x02 => '4x'
// system = encoderID & 0xF );
$encoder_version = number_format(((($encoder_id & 0xF0) >> 4) + 4500) / 1000, 3); return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
$encoder_system_id = ($encoder_id & 0x0F); }
static $lookup = array (
0x00 => 'Windows console',
0x01 => 'Linux console',
0x0F => 'unknown'
);
return $encoder_version.' ('.(isset($lookup[$encoder_system_id]) ? $lookup[$encoder_system_id] : 'undefined encoder type (0x'.dechex($encoder_system_id).')').')';
}
public static function OptimFROGcompressionLookup($compression_id) {
// mode = compression >> 3
// speedup = compression & 0x07
$compression_mode_id = ($compression_id & 0xF8) >> 3;
//$compression_speed_up_id = ($compression_id & 0x07);
static $lookup = 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($lookup[$compression_mode_id]) ? $lookup[$compression_mode_id] : 'undefined mode (0x'.str_pad(dechex($compression_mode_id), 2, '0', STR_PAD_LEFT).')');
}
public static function OptimFROGspeedupLookup($compression_id) {
// mode = compression >> 3
// speedup = compression & 0x07
//$compression_mode_id = ($compression_id & 0xF8) >> 3;
$compression_speed_up_id = ($compression_id & 0x07);
static $lookup = array (
0x00 => '1x',
0x01 => '2x',
0x02 => '4x'
);
return (isset($lookup[$compression_speed_up_id]) ? $lookup[$compression_speed_up_id] : 'undefined mode (0x'.dechex($compression_speed_up_id));
}
} }

View file

@ -1,101 +1,91 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.shorten.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Shorten Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.rkau.php |
// | Module for analyzing RKAU Audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.rkau.php,v 1.2 2006/11/02 10:48:01 ah Exp $
class getid3_rkau
class getid3_rkau extends getid3_handler
{ {
public function Analyze() { function getid3_rkau(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; 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;
}
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['fileformat'] = 'rkau';
$rkau_header = fread($getid3->fp, 20); $ThisFileInfo['audio']['dataformat'] = 'rkau';
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
// Magic bytes 'RKA' $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;
}
$getid3->info['fileformat'] = 'rkau'; $ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
$getid3->info['audio']['dataformat'] = 'rkau'; $ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
$getid3->info['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
$ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
// Shortcut $ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
$getid3->info['rkau'] = array (); $this->RKAUqualityLookup($ThisFileInfo['rkau']);
$info_rkau = &$getid3->info['rkau'];
$info_rkau['raw']['version'] = getid3_lib::LittleEndian2Int(substr($rkau_header, 3, 1)); $ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
$info_rkau['version'] = '1.'.str_pad($info_rkau['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); $ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01));
if (($info_rkau['version'] > 1.07) || ($info_rkau['version'] < 1.06)) { $ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02);
throw new getid3_exception('This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$info_rkau['version'].')'); $ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04);
}
getid3_lib::ReadSequence('LittleEndian2Int', $info_rkau, $rkau_header, 4, if ($ThisFileInfo['rkau']['flags']['streaming']) {
array ( $ThisFileInfo['avdataoffset'] += 20;
'source_bytes' => 4, $ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
'sample_rate' => 4, } else {
'channels' => 1, $ThisFileInfo['avdataoffset'] += 16;
'bits_per_sample' => 1 $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(?)
$info_rkau['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($rkau_header, 14, 1)); $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'];
$quality = $info_rkau['raw']['quality'] & 0x0F; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8));
$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds'];
$info_rkau['lossless'] = (($quality == 0) ? true : false); return true;
$info_rkau['compression_level'] = (($info_rkau['raw']['quality'] & 0xF0) >> 4) + 1;
if (!$info_rkau['lossless']) {
$info_rkau['quality_setting'] = $quality;
}
$info_rkau['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rkau_header, 15, 1)); }
$info_rkau['flags']['joint_stereo'] = (bool)(!($info_rkau['raw']['flags'] & 0x01));
$info_rkau['flags']['streaming'] = (bool) ($info_rkau['raw']['flags'] & 0x02);
$info_rkau['flags']['vrq_lossy_mode'] = (bool) ($info_rkau['raw']['flags'] & 0x04);
if ($info_rkau['flags']['streaming']) {
$getid3->info['avdataoffset'] += 20;
$info_rkau['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($rkau_header, 16, 4));
}
else {
$getid3->info['avdataoffset'] += 16;
$info_rkau['compressed_bytes'] = $getid3->info['avdataend'] - $getid3->info['avdataoffset'] - 1;
}
// Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
// sometimes it's more, sometimes less. No idea why(?)
$getid3->info['audio']['lossless'] = $info_rkau['lossless']; function RKAUqualityLookup(&$RKAUdata) {
$getid3->info['audio']['channels'] = $info_rkau['channels']; $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
$getid3->info['audio']['bits_per_sample'] = $info_rkau['bits_per_sample']; $quality = $RKAUdata['raw']['quality'] & 0x0F;
$getid3->info['audio']['sample_rate'] = $info_rkau['sample_rate'];
$getid3->info['playtime_seconds'] = $info_rkau['source_bytes'] / ($info_rkau['sample_rate'] * $info_rkau['channels'] * ($info_rkau['bits_per_sample'] / 8)); $RKAUdata['lossless'] = (($quality == 0) ? true : false);
$getid3->info['audio']['bitrate'] = ($info_rkau['compressed_bytes'] * 8) / $getid3->info['playtime_seconds']; $RKAUdata['compression_level'] = $level + 1;
if (!$RKAUdata['lossless']) {
$RKAUdata['quality_setting'] = $quality;
}
return true; return true;
}
}
} }

View file

@ -1,121 +1,179 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.shorten.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Shorten Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.shorten.php |
// | Module for analyzing Shorten Audio files |
// | dependencies: module.audio-video.riff.php |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.shorten.php,v 1.5 2006/12/03 19:28:18 ah Exp $
class getid3_shorten
class getid3_shorten extends getid3_handler
{ {
public function __construct(getID3 $getid3) { function getid3_shorten(&$fd, &$ThisFileInfo) {
parent::__construct($getid3); fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
if ((bool)ini_get('safe_mode')) { $ShortenHeader = fread($fd, 8);
throw new getid3_exception('PHP running in Safe Mode - backtick operator not available, cannot analyze Shorten files.'); 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';
if (!`head --version`) { $ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
throw new getid3_exception('head[.exe] binary not found in path. UNIX: typically /usr/bin. Windows: typically c:\windows\system32.');
}
if (!`shorten -l`) { fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET);
throw new getid3_exception('shorten[.exe] binary not found in path. UNIX: typically /usr/bin. Windows: typically c:\windows\system32.'); $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;
public function Analyze() { } else {
$getid3 = $this->getid3; // 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;
$getid3->include_module('audio-video.riff'); $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;
//}
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); }
$shn_header = fread($getid3->fp, 8); }
// Magic bytes: "ajkg" if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
$ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
return false;
}
$getid3->info['fileformat'] = 'shn'; if (GETID3_OS_ISWINDOWS) {
$getid3->info['audio']['dataformat'] = 'shn';
$getid3->info['audio']['lossless'] = true;
$getid3->info['audio']['bitrate_mode'] = 'vbr';
$getid3->info['shn']['version'] = getid3_lib::LittleEndian2Int($shn_header{4}); $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);
fseek($getid3->fp, $getid3->info['avdataend'] - 12, SEEK_SET); } else {
$seek_table_signature_test = fread($getid3->fp, 12); static $shorten_present;
if (!isset($shorten_present)) {
$getid3->info['shn']['seektable']['present'] = (bool)(substr($seek_table_signature_test, 4, 8) == 'SHNAMPSK'); $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
if ($getid3->info['shn']['seektable']['present']) {
$getid3->info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($seek_table_signature_test, 0, 4));
$getid3->info['shn']['seektable']['offset'] = $getid3->info['avdataend'] - $getid3->info['shn']['seektable']['length'];
fseek($getid3->fp, $getid3->info['shn']['seektable']['offset'], SEEK_SET);
$seek_table_magic = fread($getid3->fp, 4);
if ($seek_table_magic != 'SEEK') {
throw new getid3_exception('Expecting "SEEK" at offset '.$getid3->info['shn']['seektable']['offset'].', found "'.$seek_table_magic.'"');
} }
if (!$shorten_present) {
$seek_table_data = fread($getid3->fp, $getid3->info['shn']['seektable']['length'] - 16); $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin';
$getid3->info['shn']['seektable']['entry_count'] = floor(strlen($seek_table_data) / 80); return false;
}
$commandline = 'shorten -x '.escapeshellarg(realpath($getid3->filename)).' - | head -c 64';
$output = `$commandline`;
if (@$output && substr($output, 12, 4) == 'fmt ') {
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
$decoded_wav_format_ex = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size));
$getid3->info['audio']['channels'] = $decoded_wav_format_ex['channels'];
$getid3->info['audio']['bits_per_sample'] = $decoded_wav_format_ex['bits_per_sample'];
$getid3->info['audio']['sample_rate'] = $decoded_wav_format_ex['sample_rate'];
if (substr($output, 20 + $fmt_size, 4) == 'data') {
$getid3->info['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $decoded_wav_format_ex['raw']['nAvgBytesPerSec'];
} else {
throw new getid3_exception('shorten failed to decode DATA chunk to expected location, cannot determine playtime');
} }
$commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64';
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / $getid3->info['playtime_seconds']) * 8; }
} else { $output = `$commandline`;
throw new getid3_exception('shorten failed to decode file to WAV for parsing'); if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
return false;
}
return true; 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;
}
} }

View file

@ -1,124 +1,105 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.tta.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing TTA Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.tta.php |
// | Module for analyzing TTA Audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.tta.php,v 1.2 2006/11/02 10:48:01 ah Exp $
class getid3_tta
class getid3_tta extends getid3_handler
{ {
public function Analyze() { function getid3_tta(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; $ThisFileInfo['fileformat'] = 'tta';
$ThisFileInfo['audio']['dataformat'] = 'tta';
$ThisFileInfo['audio']['lossless'] = true;
$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$getid3->info['fileformat'] = 'tta'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->info['audio']['dataformat'] = 'tta'; $ttaheader = fread($fd, 26);
$getid3->info['audio']['lossless'] = true;
$getid3->info['audio']['bitrate_mode'] = 'vbr';
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3);
$tta_header = fread($getid3->fp, 26); 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;
}
$getid3->info['tta']['magic'] = 'TTA'; // Magic bytes 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;
switch ($tta_header{3}) { $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));
case "\x01": // TTA v1.x $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
case "\x02": // TTA v1.x $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate'];
case "\x03": // TTA v1.x break;
// "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." case '2': // TTA v2.x
$getid3->info['tta']['major_version'] = 1; // "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."
$getid3->info['avdataoffset'] += 16; $ThisFileInfo['tta']['major_version'] = 2;
$ThisFileInfo['avdataoffset'] += 20;
getid3_lib::ReadSequence('LittleEndian2Int', $getid3->info['tta'], $tta_header, 4, $ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
array ( $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
'channels' => 2, $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
'bits_per_sample' => 2, $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
'sample_rate' => 4, $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
'samples_per_channel' => 4 $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
)
);
$getid3->info['tta']['compression_level'] = ord($tta_header{3});
$getid3->info['audio']['encoder_options'] = '-e'.$getid3->info['tta']['compression_level']; $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level'];
$getid3->info['playtime_seconds'] = $getid3->info['tta']['samples_per_channel'] / $getid3->info['tta']['sample_rate']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
break; break;
case '2': // TTA v2.x case '1': // TTA v3.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." // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
$getid3->info['tta']['major_version'] = 2; $ThisFileInfo['tta']['major_version'] = 3;
$getid3->info['avdataoffset'] += 20; $ThisFileInfo['avdataoffset'] += 26;
getid3_lib::ReadSequence('LittleEndian2Int', $getid3->info['tta'], $tta_header, 4, $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup()
array ( $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
'compression_level' => 2, $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
'audio_format' => 2, $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
'channels' => 2, $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
'bits_per_sample' => 2, $ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
'sample_rate' => 4, $ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
'data_length' => 4
)
);
$getid3->info['audio']['encoder_options'] = '-e'.$getid3->info['tta']['compression_level']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
$getid3->info['playtime_seconds'] = $getid3->info['tta']['data_length'] / $getid3->info['tta']['sample_rate']; break;
break;
case '1': // TTA v3.x default:
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." $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};
$getid3->info['tta']['major_version'] = 3; return false;
$getid3->info['avdataoffset'] += 26; break;
}
getid3_lib::ReadSequence('LittleEndian2Int', $getid3->info['tta'], $tta_header, 4, $ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version'];
array ( $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample'];
'audio_format' => 2, $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate'];
'channels' => 2, $ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels'];
'bits_per_sample'=> 2, $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
'sample_rate' => 4,
'data_length' => 4,
'crc32_footer' => -4, // string
'seek_point' => 4
)
);
$getid3->info['playtime_seconds'] = $getid3->info['tta']['data_length'] / $getid3->info['tta']['sample_rate']; return true;
break; }
default:
throw new getid3_exception('This version of getID3() only knows how to handle TTA v1, v2 and v3 - it may not work correctly with this file which appears to be TTA v'.$tta_header{3});
return false;
break;
}
$getid3->info['audio']['encoder'] = 'TTA v'.$getid3->info['tta']['major_version'];
$getid3->info['audio']['bits_per_sample'] = $getid3->info['tta']['bits_per_sample'];
$getid3->info['audio']['sample_rate'] = $getid3->info['tta']['sample_rate'];
$getid3->info['audio']['channels'] = $getid3->info['tta']['channels'];
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
return true;
}
} }

View file

@ -1,239 +1,203 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.voc.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Creative VOC Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.voc.php |
// | Module for analyzing Creative VOC Audio files. |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.voc.php,v 1.3 2006/11/02 10:48:02 ah Exp $
class getid3_voc
class getid3_voc extends getid3_handler
{ {
public function Analyze() { function getid3_voc(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; $OriginalAVdataOffset = $ThisFileInfo['avdataoffset'];
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$VOCheader = fread($fd, 26);
$original_av_data_offset = $getid3->info['avdataoffset']; 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;
}
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); // shortcuts
$voc_header= fread($getid3->fp, 26); $thisfile_audio = &$ThisFileInfo['audio'];
$ThisFileInfo['voc'] = array();
$thisfile_voc = &$ThisFileInfo['voc'];
// Magic bytes: 'Creative Voice File' $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
$info_audio = &$getid3->info['audio']; // byte # Description
$getid3->info['voc'] = array (); // ------ ------------------------------------------
$info_voc = &$getid3->info['voc']; // 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)
$getid3->info['fileformat'] = 'voc'; $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
$info_audio['dataformat'] = 'voc'; $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
$info_audio['bitrate_mode'] = 'cbr'; $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
$info_audio['lossless'] = true;
$info_audio['channels'] = 1; // might be overriden below
$info_audio['bits_per_sample'] = 8; // might be overriden below
// byte # Description do {
// ------ ------------------------------------------
// 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)
getid3_lib::ReadSequence('LittleEndian2Int', $info_voc['header'], $voc_header, 20, $BlockOffset = ftell($fd);
array ( $BlockData = fread($fd, 4);
'datablock_offset' => 2, $BlockType = ord($BlockData{0});
'minor_version' => 1, $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
'major_version' => 1 $ThisBlock = array();
)
);
do { @$thisfile_voc['blocktypes'][$BlockType]++;
$block_offset = ftell($getid3->fp); switch ($BlockType) {
$block_data = fread($getid3->fp, 4); case 0: // Terminator
$block_type = ord($block_data{0}); // do nothing, we'll break out of the loop down below
$block_size = getid3_lib::LittleEndian2Int(substr($block_data, 1, 3)); break;
$this_block = array ();
@$info_voc['blocktypes'][$block_type]++; case 1: // Sound data
$BlockData .= fread($fd, 2);
if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
$ThisFileInfo['avdataoffset'] = ftell($fd);
}
fseek($fd, $BlockSize - 2, SEEK_CUR);
switch ($block_type) { $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
case 0: // Terminator $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
// do nothing, we'll break out of the loop down below if ($ThisBlock['compression_type'] <= 3) {
break; $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
}
case 1: // Sound data // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
$block_data .= fread($getid3->fp, 2); if (empty($thisfile_audio['sample_rate'])) {
if ($getid3->info['avdataoffset'] <= $original_av_data_offset) { // SR byte = 256 - (1000000 / sample_rate)
$getid3->info['avdataoffset'] = ftell($getid3->fp); $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
} }
fseek($getid3->fp, $block_size - 2, SEEK_CUR); break;
getid3_lib::ReadSequence('LittleEndian2Int', $this_block, $block_data, 4, case 2: // Sound continue
array ( case 3: // Silence
'sample_rate_id' => 1, case 4: // Marker
'compression_type' => 1 case 6: // Repeat
) case 7: // End repeat
); // nothing useful, just skip
fseek($fd, $BlockSize, SEEK_CUR);
break;
$this_block['compression_name'] = getid3_voc::VOCcompressionTypeLookup($this_block['compression_type']); case 8: // Extended
if ($this_block['compression_type'] <= 3) { $BlockData .= fread($fd, 4);
$info_voc['compressed_bits_per_sample'] = (int)(str_replace('-bit', '', $this_block['compression_name']));
}
// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) //00-01 Time Constant:
if (empty($info_audio['sample_rate'])) { // Mono: 65536 - (256000000 / sample_rate)
// SR byte = 256 - (1000000 / sample_rate) // Stereo: 65536 - (256000000 / (sample_rate * 2))
$info_audio['sample_rate'] = (int)floor((1000000 / (256 - $this_block['sample_rate_id'])) / $info_audio['channels']); $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
} $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
break; $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
case 2: // Sound continue $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
case 3: // Silence $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
case 4: // Marker break;
case 6: // Repeat
case 7: // End repeat
// nothing useful, just skip
fseek($getid3->fp, $block_size, SEEK_CUR);
break;
case 8: // Extended case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
$block_data .= fread($getid3->fp, 4); $BlockData .= fread($fd, 12);
if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
$ThisFileInfo['avdataoffset'] = ftell($fd);
}
fseek($fd, $BlockSize - 12, SEEK_CUR);
//00-01 Time Constant: $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
// Mono: 65536 - (256000000 / sample_rate) $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
// Stereo: 65536 - (256000000 / (sample_rate * 2)) $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
getid3_lib::ReadSequence('LittleEndian2Int', $this_block, $block_data, 4, $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
array (
'time_constant' => 2,
'pack_method' => 1,
'stereo' => 1
)
);
$this_block['stereo'] = (bool)$this_block['stereo'];
$info_audio['channels'] = ($this_block['stereo'] ? 2 : 1); $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
$info_audio['sample_rate'] = (int)floor((256000000 / (65536 - $this_block['time_constant'])) / $info_audio['channels']); if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
break; $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
}
case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
$block_data .= fread($getid3->fp, 12); $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
if ($getid3->info['avdataoffset'] <= $original_av_data_offset) { $thisfile_audio['channels'] = $ThisBlock['channels'];
$getid3->info['avdataoffset'] = ftell($getid3->fp); break;
}
fseek($getid3->fp, $block_size - 12, SEEK_CUR);
getid3_lib::ReadSequence('LittleEndian2Int', $this_block, $block_data, 4, default:
array ( $ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
'sample_rate' => 4, fseek($fd, $BlockSize, SEEK_CUR);
'bits_per_sample' => 1, break;
'channels' => 1, }
'wFormat' => 2
)
);
$this_block['compression_name'] = getid3_voc::VOCwFormatLookup($this_block['wFormat']); if (!empty($ThisBlock)) {
if (getid3_voc::VOCwFormatActualBitsPerSampleLookup($this_block['wFormat'])) { $ThisBlock['block_offset'] = $BlockOffset;
$info_voc['compressed_bits_per_sample'] = getid3_voc::VOCwFormatActualBitsPerSampleLookup($this_block['wFormat']); $ThisBlock['block_size'] = $BlockSize;
} $ThisBlock['block_type_id'] = $BlockType;
$thisfile_voc['blocks'][] = $ThisBlock;
}
$info_audio['sample_rate'] = $this_block['sample_rate']; } while (!feof($fd) && ($BlockType != 0));
$info_audio['bits_per_sample'] = $this_block['bits_per_sample'];
$info_audio['channels'] = $this_block['channels'];
break;
default: // Terminator block doesn't have size field, so seek back 3 spaces
$getid3->warning('Unhandled block type "'.$block_type.'" at offset '.$block_offset); fseek($fd, -3, SEEK_CUR);
fseek($getid3->fp, $block_size, SEEK_CUR);
break;
}
if (!empty($this_block)) { ksort($thisfile_voc['blocktypes']);
$this_block['block_offset'] = $block_offset;
$this_block['block_size'] = $block_size;
$this_block['block_type_id'] = $block_type;
$info_voc['blocks'][] = $this_block;
}
} while (!feof($getid3->fp) && ($block_type != 0)); 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'];
}
// Terminator block doesn't have size field, so seek back 3 spaces return true;
fseek($getid3->fp, -3, SEEK_CUR); }
ksort($info_voc['blocktypes']); 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');
}
if (!empty($info_voc['compressed_bits_per_sample'])) { function VOCwFormatLookup($index) {
$getid3->info['playtime_seconds'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / ($info_voc['compressed_bits_per_sample'] * $info_audio['channels'] * $info_audio['sample_rate']); static $VOCwFormatLookup = array(
$info_audio['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds']; 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);
}
return true; function VOCwFormatActualBitsPerSampleLookup($index) {
} static $VOCwFormatLookup = array(
0x0000 => 8,
0x0001 => 4,
0x0002 => 3,
public static function VOCcompressionTypeLookup($index) { 0x0003 => 2,
0x0004 => 16,
static $lookup = array ( 0x0006 => 8,
0 => '8-bit', 0x0007 => 8,
1 => '4-bit', 0x2000 => 4
2 => '2.6-bit', );
3 => '2-bit' return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
); }
return (isset($lookup[$index]) ? $lookup[$index] : 'Multi DAC ('.($index - 3).') channels');
}
public static function VOCwFormatLookup($index) {
static $lookup = 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($lookup[$index]) ? $lookup[$index] : false);
}
public static function VOCwFormatActualBitsPerSampleLookup($index) {
static $lookup = array (
0x0000 => 8,
0x0001 => 4,
0x0002 => 3,
0x0003 => 2,
0x0004 => 16,
0x0006 => 8,
0x0007 => 8,
0x2000 => 4
);
return (isset($lookup[$index]) ? $lookup[$index] : false);
}
} }

View file

@ -1,163 +1,157 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.vqf.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing VQF audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.vqf.php |
// | Module for analyzing VQF Audio files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.vqf.php,v 1.3 2006/11/16 23:16:31 ah Exp $
class getid3_vqf
class getid3_vqf extends getid3_handler
{ {
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
public function Analyze() { $ThisFileInfo['fileformat'] = 'vqf';
$ThisFileInfo['audio']['dataformat'] = 'vqf';
$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
$ThisFileInfo['audio']['lossless'] = false;
$getid3 = $this->getid3; // shortcut
$ThisFileInfo['vqf']['raw'] = array();
$thisfile_vqf = &$ThisFileInfo['vqf'];
$thisfile_vqf_raw = &$thisfile_vqf['raw'];
// based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de> fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html $VQFheaderData = fread($fd, 16);
$getid3->info['fileformat'] = 'vqf'; $offset = 0;
$getid3->info['audio']['dataformat'] = 'vqf'; $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
$getid3->info['audio']['bitrate_mode'] = 'cbr'; if ($thisfile_vqf_raw['header_tag'] != 'TWIN') {
$getid3->info['audio']['lossless'] = false; $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;
// Shortcuts while (ftell($fd) < $ThisFileInfo['avdataend']) {
$getid3->info['vqf']['raw'] = array ();
$info_vqf = &$getid3->info['vqf'];
$info_vqf_raw = &$info_vqf['raw'];
// Get header $ChunkBaseOffset = ftell($fd);
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $chunkoffset = 0;
$vqf_header_data = fread($getid3->fp, 16); $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);
}
$info_vqf_raw['header_tag'] = 'TWIN'; // Magic bytes switch ($ChunkName) {
$info_vqf_raw['version'] = substr($vqf_header_data, 4, 8); case 'COMM':
$info_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($vqf_header_data, 12, 4)); // shortcut
$thisfile_vqf['COMM'] = array();
$thisfile_vqf_COMM = &$thisfile_vqf['COMM'];
while (ftell($getid3->fp) < $getid3->info['avdataend']) { $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;
$chunk_base_offset = ftell($getid3->fp); $ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
$chunk_data = fread($getid3->fp, 8); $ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
$chunk_name = substr($chunk_data, 0, 4); $ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
$ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000);
if ($chunk_name == 'DATA') { if ($ThisFileInfo['audio']['bitrate'] == 0) {
$getid3->info['avdataoffset'] = $chunk_base_offset; $ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
break; return false;
} }
break;
$chunk_size = getid3_lib::BigEndian2Int(substr($chunk_data, 4, 4)); case 'NAME':
if ($chunk_size > ($getid3->info['avdataend'] - ftell($getid3->fp))) { case 'AUTH':
throw new getid3_exception('Invalid chunk size ('.$chunk_size.') for chunk "'.$chunk_name.'" at offset 8.'); case '(c) ':
} case 'FILE':
if ($chunk_size > 0) { case 'COMT':
$chunk_data .= fread($getid3->fp, $chunk_size); case 'ALBM':
} $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
break;
switch ($chunk_name) { case 'DSIZ':
$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
break;
case 'COMM': default:
$info_vqf['COMM'] = array (); $ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
getid3_lib::ReadSequence('BigEndian2Int', $info_vqf['COMM'], $chunk_data, 8, break;
array ( }
'channel_mode' => 4, }
'bitrate' => 4,
'sample_rate' => 4,
'security_level' => 4
)
);
$getid3->info['audio']['channels'] = $info_vqf['COMM']['channel_mode'] + 1; $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
$getid3->info['audio']['sample_rate'] = getid3_vqf::VQFchannelFrequencyLookup($info_vqf['COMM']['sample_rate']);
$getid3->info['audio']['bitrate'] = $info_vqf['COMM']['bitrate'] * 1000;
$getid3->info['audio']['encoder_options'] = 'CBR' . ceil($getid3->info['audio']['bitrate']/1000);
if ($getid3->info['audio']['bitrate'] == 0) { if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) {
throw new getid3_exception('Corrupt VQF file: bitrate_audio == zero'); switch ($thisfile_vqf['DSIZ']) {
} case 0:
break; 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;
case 'NAME': default:
case 'AUTH': $ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'));
case '(c) ': break;
case 'FILE': }
case 'COMT': }
case 'ALBM':
$info_vqf['comments'][getid3_vqf::VQFcommentNiceNameLookup($chunk_name)][] = trim(substr($chunk_data, 8));
break;
case 'DSIZ': return true;
$info_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($chunk_data, 8, 4)); }
break;
default: function VQFchannelFrequencyLookup($frequencyid) {
$getid3->warning('Unhandled chunk type "'.$chunk_name.'" at offset 8'); static $VQFchannelFrequencyLookup = array(
break; 11 => 11025,
} 22 => 22050,
} 44 => 44100
);
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
}
$getid3->info['playtime_seconds'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['audio']['bitrate']; function VQFcommentNiceNameLookup($shortname) {
static $VQFcommentNiceNameLookup = array(
if (isset($info_vqf['DSIZ']) && (($info_vqf['DSIZ'] != ($getid3->info['avdataend'] - $getid3->info['avdataoffset'] - strlen('DATA'))))) { 'NAME' => 'title',
switch ($info_vqf['DSIZ']) { 'AUTH' => 'artist',
case 0: '(c) ' => 'copyright',
case 1: 'FILE' => 'filename',
$getid3->warning('Invalid DSIZ value "'.$info_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'.($info_vqf['DSIZ'] + 1).'.0'); 'COMT' => 'comment',
$getid3->info['audio']['encoder'] = 'Ahead Nero'; 'ALBM' => 'album'
break; );
return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
default: }
$getid3->warning('Probable corrupted file - should be '.$info_vqf['DSIZ'].' bytes, actually '.($getid3->info['avdataend'] - $getid3->info['avdataoffset'] - strlen('DATA')));
break;
}
}
return true;
}
public static function VQFchannelFrequencyLookup($frequencyid) {
static $lookup = array (
11 => 11025,
22 => 22050,
44 => 44100
);
return (isset($lookup[$frequencyid]) ? $lookup[$frequencyid] : $frequencyid * 1000);
}
public static function VQFcommentNiceNameLookup($shortname) {
static $lookup = array (
'NAME' => 'title',
'AUTH' => 'artist',
'(c) ' => 'copyright',
'FILE' => 'filename',
'COMT' => 'comment',
'ALBM' => 'album'
);
return (isset($lookup[$shortname]) ? $lookup[$shortname] : $shortname);
}
} }

View file

@ -1,398 +1,370 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.audio.wavpack.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing WavPack v4.0+ Audio files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.audio.wavpack.php |
// | module for analyzing WavPack v4.0+ Audio files |
// | dependencies: audio-video.riff |
// +----------------------------------------------------------------------+
//
// $Id: module.audio.wavpack.php,v 1.2 2006/11/02 10:48:02 ah Exp $
class getid3_wavpack extends getid3_handler class getid3_wavpack
{ {
public function Analyze() { function getid3_wavpack(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$getid3->include_module('audio-video.riff'); while (true) {
$getid3->info['wavpack'] = array (); $wavpackheader = fread($fd, 32);
$info_wavpack = &$getid3->info['wavpack'];
if (ftell($fd) >= $ThisFileInfo['avdataend']) {
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); break;
} elseif (feof($fd)) {
while (true) { break;
} elseif (
$wavpack_header = fread($getid3->fp, 32); (@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) &&
(@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) &&
if (ftell($getid3->fp) >= $getid3->info['avdataend']) { (!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) &&
break; ((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) {
} elseif (feof($getid3->fp)) { break;
break; }
} elseif (
(@$info_wavpack_blockheader['total_samples'] > 0) && $blockheader_offset = ftell($fd) - 32;
(@$info_wavpack_blockheader['block_samples'] > 0) && $blockheader_magic = substr($wavpackheader, 0, 4);
(!isset($info_wavpack['riff_trailer_size']) || ($info_wavpack['riff_trailer_size'] <= 0)) && $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
((@$info_wavpack['config_flags']['md5_checksum'] === false) || !empty($getid3->info['md5_data_source']))) {
break; if ($blockheader_magic != 'wvpk') {
} $ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"';
if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
$block_header_offset = ftell($getid3->fp) - 32; unset($ThisFileInfo['fileformat']);
$block_header_magic = substr($wavpack_header, 0, 4); unset($ThisFileInfo['audio']);
$block_header_size = getid3_lib::LittleEndian2Int(substr($wavpack_header, 4, 4)); unset($ThisFileInfo['wavpack']);
}
if ($block_header_magic != 'wvpk') { return false;
throw new getid3_exception('Expecting "wvpk" at offset '.$block_header_offset.', found "'.$block_header_magic.'"'); }
}
if ((@$info_wavpack_blockheader['block_samples'] <= 0) || (@$info_wavpack_blockheader['total_samples'] <= 0)) { if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) ||
(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) {
// Also, it is possible that the first block might not have // Also, it is possible that the first block might not have
// any samples (block_samples == 0) and in this case you should skip blocks // any samples (block_samples == 0) and in this case you should skip blocks
// until you find one with samples because the other information (like // until you find one with samples because the other information (like
// total_samples) are not guaranteed to be correct until (block_samples > 0) // 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 // 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 // (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 // total_samples will be -1 and you must seek to the final block to determine
// the total number of samples. // the total number of samples.
$getid3->info['audio']['dataformat'] = 'wavpack'; $ThisFileInfo['audio']['dataformat'] = 'wavpack';
$getid3->info['fileformat'] = 'wavpack'; $ThisFileInfo['fileformat'] = 'wavpack';
$getid3->info['audio']['lossless'] = true; $ThisFileInfo['audio']['lossless'] = true;
$getid3->info['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
$info_wavpack['blockheader']['offset'] = $block_header_offset; $ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset;
$info_wavpack['blockheader']['magic'] = $block_header_magic; $ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic;
$info_wavpack['blockheader']['size'] = $block_header_size; $ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size;
$info_wavpack_blockheader = &$info_wavpack['blockheader'];
if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) {
if ($info_wavpack_blockheader['size'] >= 0x100000) { $ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
throw new getid3_exception('Expecting WavPack block size less than "0x100000", found "'.$info_wavpack_blockheader['size'].'" at offset '.$info_wavpack_blockheader['offset']); if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
} unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['audio']);
$info_wavpack_blockheader['minor_version'] = ord($wavpack_header{8}); unset($ThisFileInfo['wavpack']);
$info_wavpack_blockheader['major_version'] = ord($wavpack_header{9}); }
return false;
if (($info_wavpack_blockheader['major_version'] != 4) || }
(($info_wavpack_blockheader['minor_version'] < 4) &&
($info_wavpack_blockheader['minor_version'] > 16))) { $ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
throw new getid3_exception('Expecting WavPack version between "4.2" and "4.16", found version "'.$info_wavpack_blockheader['major_version'].'.'.$info_wavpack_blockheader['minor_version'].'" at offset '.$info_wavpack_blockheader['offset']); $ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
}
if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) ||
$info_wavpack_blockheader['track_number'] = ord($wavpack_header{10}); // unused (($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) &&
$info_wavpack_blockheader['index_number'] = ord($wavpack_header{11}); // unused ($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'];
getid3_lib::ReadSequence('LittleEndian2Int', $info_wavpack_blockheader, $wavpack_header, 12, if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
array ( unset($ThisFileInfo['fileformat']);
'total_samples' => 4, unset($ThisFileInfo['audio']);
'block_index' => 4, unset($ThisFileInfo['wavpack']);
'block_samples' => 4, }
'flags_raw' => 4, return false;
'crc' => 4 }
)
); $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));
$info_wavpack_blockheader['flags']['bytes_per_sample'] = 1 + ($info_wavpack_blockheader['flags_raw'] & 0x00000003); $ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
$info_wavpack_blockheader['flags']['mono'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000004); $ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
$info_wavpack_blockheader['flags']['hybrid'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000008); $ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
$info_wavpack_blockheader['flags']['joint_stereo'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000010); $ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
$info_wavpack_blockheader['flags']['cross_decorrelation'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000020);
$info_wavpack_blockheader['flags']['hybrid_noiseshape'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000040); $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003);
$info_wavpack_blockheader['flags']['ieee_32bit_float'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000080); $ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004);
$info_wavpack_blockheader['flags']['int_32bit'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000100); $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008);
$info_wavpack_blockheader['flags']['hybrid_bitrate_noise'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000200); $ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010);
$info_wavpack_blockheader['flags']['hybrid_balance_noise'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000400); $ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020);
$info_wavpack_blockheader['flags']['multichannel_initial'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00000800); $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040);
$info_wavpack_blockheader['flags']['multichannel_final'] = (bool) ($info_wavpack_blockheader['flags_raw'] & 0x00001000); $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);
$getid3->info['audio']['lossless'] = !$info_wavpack_blockheader['flags']['hybrid']; $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);
while (!feof($getid3->fp) && (ftell($getid3->fp) < ($block_header_offset + $block_header_size + 8))) {
$ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'];
$metablock = array('offset'=>ftell($getid3->fp)); }
$metablockheader = fread($getid3->fp, 2);
if (feof($getid3->fp)) { while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) {
break;
} $metablock = array('offset'=>ftell($fd));
$metablock['id'] = ord($metablockheader{0}); $metablockheader = fread($fd, 2);
$metablock['function_id'] = ($metablock['id'] & 0x3F); if (feof($fd)) {
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']); break;
}
// The 0x20 bit in the id of the meta subblocks (which is defined as $metablock['id'] = ord($metablockheader{0});
// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that $metablock['function_id'] = ($metablock['id'] & 0x3F);
// if a decoder encounters an id that it does not know about, it uses $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
// 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 // The 0x20 bit in the id of the meta subblocks (which is defined as
// then the decoder should quit because it means that an understanding // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
// of the metadata is required to correctly decode the audio. // 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
$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20); // then the decoder simply ignores the metadata, but if it is zero
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); // then the decoder should quit because it means that an understanding
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80); // of the metadata is required to correctly decode the audio.
if ($metablock['large_block']) { $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
$metablockheader .= fread($getid3->fp, 2);
} $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words $metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
$metablock['data'] = null; if ($metablock['large_block']) {
$metablockheader .= fread($fd, 2);
if ($metablock['size'] > 0) { }
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
switch ($metablock['function_id']) { $metablock['data'] = null;
case 0x21: // ID_RIFF_HEADER if ($metablock['size'] > 0) {
case 0x22: // ID_RIFF_TRAILER
case 0x23: // ID_REPLAY_GAIN switch ($metablock['function_id']) {
case 0x24: // ID_CUESHEET case 0x21: // ID_RIFF_HEADER
case 0x25: // ID_CONFIG_BLOCK case 0x22: // ID_RIFF_TRAILER
case 0x26: // ID_MD5_CHECKSUM case 0x23: // ID_REPLAY_GAIN
$metablock['data'] = fread($getid3->fp, $metablock['size']); case 0x24: // ID_CUESHEET
case 0x25: // ID_CONFIG_BLOCK
if ($metablock['padded_data']) { case 0x26: // ID_MD5_CHECKSUM
// padded to the nearest even byte $metablock['data'] = fread($fd, $metablock['size']);
$metablock['size']--;
$metablock['data'] = substr($metablock['data'], 0, -1); if ($metablock['padded_data']) {
} // padded to the nearest even byte
break; $metablock['size']--;
$metablock['data'] = substr($metablock['data'], 0, -1);
}
case 0x00: // ID_DUMMY break;
case 0x01: // ID_ENCODER_INFO
case 0x02: // ID_DECORR_TERMS case 0x00: // ID_DUMMY
case 0x03: // ID_DECORR_WEIGHTS case 0x01: // ID_ENCODER_INFO
case 0x04: // ID_DECORR_SAMPLES case 0x02: // ID_DECORR_TERMS
case 0x05: // ID_ENTROPY_VARS case 0x03: // ID_DECORR_WEIGHTS
case 0x06: // ID_HYBRID_PROFILE case 0x04: // ID_DECORR_SAMPLES
case 0x07: // ID_SHAPING_WEIGHTS case 0x05: // ID_ENTROPY_VARS
case 0x08: // ID_FLOAT_INFO case 0x06: // ID_HYBRID_PROFILE
case 0x09: // ID_INT32_INFO case 0x07: // ID_SHAPING_WEIGHTS
case 0x0A: // ID_WV_BITSTREAM case 0x08: // ID_FLOAT_INFO
case 0x0B: // ID_WVC_BITSTREAM case 0x09: // ID_INT32_INFO
case 0x0C: // ID_WVX_BITSTREAM case 0x0A: // ID_WV_BITSTREAM
case 0x0D: // ID_CHANNEL_INFO case 0x0B: // ID_WVC_BITSTREAM
fseek($getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); case 0x0C: // ID_WVX_BITSTREAM
break; case 0x0D: // ID_CHANNEL_INFO
fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
break;
default:
$getid3->warning('Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']); default:
fseek($getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); $ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
break; fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
} break;
}
switch ($metablock['function_id']) { switch ($metablock['function_id']) {
case 0x21: // ID_RIFF_HEADER
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));
$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4)); getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader);
$metablock['riff'] = $ParsedRIFFheader['riff'];
// Clone getid3 $metablock['riff']['original_filesize'] = $original_wav_filesize;
$clone = clone $getid3; $ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
// Analyze clone by string $ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec'];
$riff = new getid3_riff($clone); $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
$riff->AnalyzeString($metablock['data']);
// Safe RIFF header in case there's a RIFF footer later
// Import from clone and destroy $metablockRIFFheader = $metablock['data'];
$metablock['riff'] = $clone->info['riff']; break;
$getid3->warnings($clone->warnings());
unset($clone);
case 0x22: // ID_RIFF_TRAILER
// Save RIFF header - we may need it later for RIFF footer parsing $metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
$this->riff_header = $metablock['data']; getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$metablock['riff']['original_filesize'] = $original_wav_filesize; $ftell_old = ftell($fd);
$info_wavpack['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
$ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array());
$getid3->info['audio']['sample_rate'] = $metablock['riff']['raw']['fmt ']['nSamplesPerSec']; $metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter);
$getid3->info['playtime_seconds'] = $info_wavpack_blockheader['total_samples'] / $getid3->info['audio']['sample_rate']; fseek($fd, $ftell_old, SEEK_SET);
// Safe RIFF header in case there's a RIFF footer later if (!empty($metablock['riff']['INFO'])) {
$metablock_riff_header = $metablock['data']; getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']);
break; $ThisFileInfo['tags']['riff'] = $metablock['comments'];
}
break;
case 0x22: // ID_RIFF_TRAILER
$metablock_riff_footer = $metablock_riff_header.$metablock['data']; case 0x23: // ID_REPLAY_GAIN
$ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
$start_offset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); break;
$ftell_old = ftell($getid3->fp);
case 0x24: // ID_CUESHEET
// Clone getid3 $ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
$clone = clone $getid3; break;
// Call public method that really should be private
$riff = new getid3_riff($clone); case 0x25: // ID_CONFIG_BLOCK
$metablock['riff'] = $riff->ParseRIFF($start_offset, $start_offset + $metablock['size']); $metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
unset($clone);
$metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats
fseek($getid3->fp, $ftell_old, SEEK_SET); $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode
$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
if (!empty($metablock['riff']['INFO'])) { $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
getid3_riff::RIFFCommentsParse($metablock['riff']['INFO'], $metablock['comments']); $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
$getid3->info['tags']['riff'] = $metablock['comments']; $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
break; $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
case 0x23: // ID_REPLAY_GAIN $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
$getid3->warning('WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']); $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
break; $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)
case 0x24: // ID_CUESHEET $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
$getid3->warning('WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']); $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
break; $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
case 0x25: // ID_CONFIG_BLOCK $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress %
$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
$ThisFileInfo['wavpack']['config_flags'] = $metablock['flags'];
$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 if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) {
$metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode @$ThisFileInfo['audio']['encoder_options'] .= ' -b???';
$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 @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : '');
$metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : '');
$metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : '');
$metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : '');
$metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
$metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : '');
$metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : '');
$metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : '');
$metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
$metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet) @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : '');
$metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode if (@$ThisFileInfo['audio']['encoder_options']) {
$metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information) $ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']);
$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 elseif (isset($ThisFileInfo['audio']['encoder_options'])) {
$metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature unset($ThisFileInfo['audio']['encoder_options']);
$metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % }
break;
$info_wavpack['config_flags'] = $metablock['flags'];
$getid3->info['audio']['encoder_options'] = trim( case 0x26: // ID_MD5_CHECKSUM
($info_wavpack_blockheader['flags']['hybrid'] ? ' -b???' : '') . if (strlen($metablock['data']) == 16) {
($metablock['flags']['adobe_mode'] ? ' -a' : '') . $ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
($metablock['flags']['optimize_wvc'] ? ' -cc' : '') . } else {
($metablock['flags']['create_exe'] ? ' -e' : '') . $ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes';
($metablock['flags']['fast_flag'] ? ' -f' : '') . }
($metablock['flags']['joint_override'] ? ' -j?' : '') . break;
($metablock['flags']['high_flag'] ? ' -h' : '') .
($metablock['flags']['md5_checksum'] ? ' -m' : '') .
($metablock['flags']['calc_noise'] ? ' -n' : '') . case 0x00: // ID_DUMMY
($metablock['flags']['shape_override'] ? ' -s?' : '') . case 0x01: // ID_ENCODER_INFO
($metablock['flags']['extra_mode'] ? ' -x?' : '') case 0x02: // ID_DECORR_TERMS
); case 0x03: // ID_DECORR_WEIGHTS
if (!$getid3->info['audio']['encoder_options']) { case 0x04: // ID_DECORR_SAMPLES
unset($getid3->info['audio']['encoder_options']); case 0x05: // ID_ENTROPY_VARS
} case 0x06: // ID_HYBRID_PROFILE
break; case 0x07: // ID_SHAPING_WEIGHTS
case 0x08: // ID_FLOAT_INFO
case 0x09: // ID_INT32_INFO
case 0x26: // ID_MD5_CHECKSUM case 0x0A: // ID_WV_BITSTREAM
if (strlen($metablock['data']) == 16) { case 0x0B: // ID_WVC_BITSTREAM
$getid3->info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); case 0x0C: // ID_WVX_BITSTREAM
} else { case 0x0D: // ID_CHANNEL_INFO
$getid3->warning('Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes'); unset($metablock);
} break;
break; }
}
case 0x00: // ID_DUMMY if (!empty($metablock)) {
case 0x01: // ID_ENCODER_INFO $ThisFileInfo['wavpack']['metablocks'][] = $metablock;
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 $ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
case 0x09: // ID_INT32_INFO $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
case 0x0A: // ID_WV_BITSTREAM $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
case 0x0B: // ID_WVC_BITSTREAM
case 0x0C: // ID_WVX_BITSTREAM if (@$ThisFileInfo['playtime_seconds']) {
case 0x0D: // ID_CHANNEL_INFO
unset($metablock); $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
break;
} } else {
} $ThisFileInfo['audio']['dataformat'] = 'wvc';
if (!empty($metablock)) { }
$info_wavpack['metablocks'][] = $metablock;
} return true;
}
}
} function WavPackMetablockNameLookup(&$id) {
static $WavPackMetablockNameLookup = array(
$getid3->info['audio']['encoder'] = 'WavPack v'.$info_wavpack_blockheader['major_version'].'.'.str_pad($info_wavpack_blockheader['minor_version'], 2, '0', STR_PAD_LEFT); 0x00 => 'Dummy',
$getid3->info['audio']['bits_per_sample'] = $info_wavpack_blockheader['flags']['bytes_per_sample'] * 8; 0x01 => 'Encoder Info',
$getid3->info['audio']['channels'] = ($info_wavpack_blockheader['flags']['mono'] ? 1 : 2); 0x02 => 'Decorrelation Terms',
0x03 => 'Decorrelation Weights',
if (@$getid3->info['playtime_seconds']) { 0x04 => 'Decorrelation Samples',
$getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds']; 0x05 => 'Entropy Variables',
} else { 0x06 => 'Hybrid Profile',
$getid3->info['audio']['dataformat'] = 'wvc'; 0x07 => 'Shaping Weights',
} 0x08 => 'Float Info',
0x09 => 'Int32 Info',
return true; 0x0A => 'WV Bitstream',
} 0x0B => 'WVC Bitstream',
0x0C => 'WVX Bitstream',
0x0D => 'Channel Info',
0x21 => 'RIFF header',
public static function WavPackMetablockNameLookup($id) { 0x22 => 'RIFF trailer',
0x23 => 'Replay Gain',
static $lookup = array( 0x24 => 'Cuesheet',
0x00 => 'Dummy', 0x25 => 'Config Block',
0x01 => 'Encoder Info', 0x26 => 'MD5 Checksum',
0x02 => 'Decorrelation Terms', );
0x03 => 'Decorrelation Weights', return (@$WavPackMetablockNameLookup[$id]);
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 (@$lookup[$id]);
}
} }

View file

@ -1,319 +1,684 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.graphic.bmp.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing BMP Image files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.graphic.bmp.php |
// | Module for analyzing BMP graphic files. |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.graphic.bmp.php,v 1.4 2006/11/02 10:48:02 ah Exp $
class getid3_bmp
class getid3_bmp extends getid3_handler
{ {
function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
public function Analyze() { // shortcuts
$ThisFileInfo['bmp']['header']['raw'] = array();
$thisfile_bmp = &$ThisFileInfo['bmp'];
$thisfile_bmp_header = &$thisfile_bmp['header'];
$thisfile_bmp_header_raw = &$thisfile_bmp_header['raw'];
$getid3 = $this->getid3; // 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;
// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
// all versions $offset = 0;
// WORD bfType; $BMPheader = fread($fd, 14 + 40);
// DWORD bfSize;
// WORD bfReserved1;
// WORD bfReserved2;
// DWORD bfOffBits;
// shortcuts $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
$getid3->info['bmp']['header']['raw'] = array (); $offset += 2;
$info_bmp = &$getid3->info['bmp'];
$info_bmp_header = &$info_bmp['header'];
$info_bmp_header_raw = &$info_bmp_header['raw'];
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
$bmp_header = fread($getid3->fp, 14 + 40); $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['bmp']);
return false;
}
// Magic bytes $thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
$info_bmp_header_raw['identifier'] = 'BM'; $offset += 4;
$thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 2, $offset += 2;
array ( $thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
'filesize' => 4, $offset += 2;
'reserved1' => 2, $thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
'reserved2' => 2, $offset += 4;
'data_offset' => 4, $thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
'header_size' => 4 $offset += 4;
)
);
// Check if the hardcoded-to-1 "planes" is at offset 22 or 26
$planes22 = getid3_lib::LittleEndian2Int(substr($bmp_header, 22, 2));
$planes26 = getid3_lib::LittleEndian2Int(substr($bmp_header, 26, 2));
if (($planes22 == 1) && ($planes26 != 1)) {
$info_bmp['type_os'] = 'OS/2';
$info_bmp['type_version'] = 1;
}
elseif (($planes26 == 1) && ($planes22 != 1)) {
$info_bmp['type_os'] = 'Windows';
$info_bmp['type_version'] = 1;
}
elseif ($info_bmp_header_raw['header_size'] == 12) {
$info_bmp['type_os'] = 'OS/2';
$info_bmp['type_version'] = 1;
}
elseif ($info_bmp_header_raw['header_size'] == 40) {
$info_bmp['type_os'] = 'Windows';
$info_bmp['type_version'] = 1;
}
elseif ($info_bmp_header_raw['header_size'] == 84) {
$info_bmp['type_os'] = 'Windows';
$info_bmp['type_version'] = 4;
}
elseif ($info_bmp_header_raw['header_size'] == 100) {
$info_bmp['type_os'] = 'Windows';
$info_bmp['type_version'] = 5;
}
else {
throw new getid3_exception('Unknown BMP subtype (or not a BMP file)');
}
$getid3->info['fileformat'] = 'bmp';
$getid3->info['video']['dataformat'] = 'bmp';
$getid3->info['video']['lossless'] = true;
$getid3->info['video']['pixel_aspect_ratio'] = (float)1;
if ($info_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 */
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 18,
array (
'width' => 2,
'height' => 2,
'planes' => 2,
'bits_per_pixel' => 2
)
);
$getid3->info['video']['resolution_x'] = $info_bmp_header_raw['width'];
$getid3->info['video']['resolution_y'] = $info_bmp_header_raw['height'];
$getid3->info['video']['codec'] = 'BI_RGB '.$info_bmp_header_raw['bits_per_pixel'].'-bit';
$getid3->info['video']['bits_per_sample'] = $info_bmp_header_raw['bits_per_pixel'];
if ($info_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 */
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 26,
array (
'compression' => 4,
'bmp_data_size' => 4,
'resolution_h' => 4,
'resolution_v' => 4,
'colors_used' => 4,
'colors_important' => 4,
'resolution_units' => 2,
'reserved1' => 2,
'recording' => 2,
'rendering' => 2,
'size1' => 4,
'size2' => 4,
'color_encoding' => 4,
'identifier' => 4
)
);
$info_bmp_header['compression'] = getid3_bmp::BMPcompressionOS2Lookup($info_bmp_header_raw['compression']);
$getid3->info['video']['codec'] = $info_bmp_header['compression'].' '.$info_bmp_header_raw['bits_per_pixel'].'-bit';
}
return true;
}
if ($info_bmp['type_os'] == 'Windows') { // 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;
}
// Windows-format BMP $ThisFileInfo['fileformat'] = 'bmp';
$ThisFileInfo['video']['dataformat'] = 'bmp';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp if ($thisfile_bmp['type_os'] == 'OS/2') {
// all versions
// DWORD biSize;
// LONG biWidth;
// LONG biHeight;
// WORD biPlanes;
// WORD biBitCount;
// DWORD biCompression;
// DWORD biSizeImage;
// LONG biXPelsPerMeter;
// LONG biYPelsPerMeter;
// DWORD biClrUsed;
// DWORD biClrImportant;
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 18, // OS/2-format BMP
array ( // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
'width' => -4, //signed
'height' => -4, //signed
'planes' => 2,
'bits_per_pixel' => 2,
'compression' => 4,
'bmp_data_size' => 4,
'resolution_h' => -4, //signed
'resolution_v' => -4, //signed
'colors_used' => 4,
'colors_important' => 4
)
);
foreach (array ('width', 'height', 'resolution_h', 'resolution_v') as $key) {
$info_bmp_header_raw[$key] = getid3_lib::LittleEndian2Int($info_bmp_header_raw[$key], true);
}
$info_bmp_header['compression'] = getid3_bmp::BMPcompressionWindowsLookup($info_bmp_header_raw['compression']); // DWORD Size; /* Size of this structure in bytes */
$getid3->info['video']['resolution_x'] = $info_bmp_header_raw['width']; // DWORD Width; /* Bitmap width in pixels */
$getid3->info['video']['resolution_y'] = $info_bmp_header_raw['height']; // DWORD Height; /* Bitmap height in pixel */
$getid3->info['video']['codec'] = $info_bmp_header['compression'].' '.$info_bmp_header_raw['bits_per_pixel'].'-bit'; // WORD NumPlanes; /* Number of bit planes (color depth) */
$getid3->info['video']['bits_per_sample'] = $info_bmp_header_raw['bits_per_pixel']; // WORD BitsPerPixel; /* Number of bits per pixel per plane */
// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
if (($info_bmp['type_version'] >= 4) || ($info_bmp_header_raw['compression'] == 3)) { $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;
}
$bmp_header .= fread($getid3->fp, 44); 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);
}
}
}
// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp if ($ExtractData) {
// Win95+, WinNT4.0+ fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET);
// DWORD bV4RedMask; $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
// DWORD bV4GreenMask; $BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength);
// DWORD bV4BlueMask; $pixeldataoffset = 0;
// DWORD bV4AlphaMask; switch (@$thisfile_bmp_header_raw['compression']) {
// DWORD bV4CSType;
// CIEXYZTRIPLE bV4Endpoints;
// DWORD bV4GammaRed;
// DWORD bV4GammaGreen;
// DWORD bV4GammaBlue;
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 54, case 0: // BI_RGB
array ( switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
'red_mask' => 4, case 1:
'green_mask' => 4, for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
'blue_mask' => 4, for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
'alpha_mask' => 4, $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
'cs_type' => 4, for ($i = 7; $i >= 0; $i--) {
'ciexyz_red' => -4, //string $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
'ciexyz_green' => -4, //string $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
'ciexyz_blue' => -4, //string $col++;
'gamma_red' => 4, }
'gamma_green' => 4, }
'gamma_blue' => 4 while (($pixeldataoffset % 4) != 0) {
) // lines are padded to nearest DWORD
); $pixeldataoffset++;
}
}
break;
$info_bmp_header['ciexyz_red'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_red'])); case 4:
$info_bmp_header['ciexyz_green'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_green'])); for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
$info_bmp_header['ciexyz_blue'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_blue'])); 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;
if ($info_bmp['type_version'] >= 5) { case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
$bmp_header .= fread($getid3->fp, 16); 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) {
// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp // escaped/absolute mode - the first byte of the pair can be set to zero to
// Win98+, Win2000+ // indicate an escape character that denotes the end of a line, the end of
// DWORD bV5Intent; // a bitmap, or a delta, depending on the value of the second byte.
// DWORD bV5ProfileData; switch ($secondbyte) {
// DWORD bV5ProfileSize; case 0:
// DWORD bV5Reserved; // end of line
// no need for special processing, just ignore
break;
getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 98, case 1:
array ( // end of bitmap
'intent' => 4, $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
'profile_data_offset' => 4, break;
'profile_data_size' => 4,
'reserved3' => 4
)
);
} 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;
return true; 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 {
throw new getid3_exception('Unknown BMP format in header.'); // 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;
public static function BMPcompressionWindowsLookup($compression_id) { 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) {
static $lookup = array ( // escaped/absolute mode - the first byte of the pair can be set to zero to
0 => 'BI_RGB', // indicate an escape character that denotes the end of a line, the end of
1 => 'BI_RLE8', // a bitmap, or a delta, depending on the value of the second byte.
2 => 'BI_RLE4', switch ($secondbyte) {
3 => 'BI_BITFIELDS', case 0:
4 => 'BI_JPEG', // end of line
5 => 'BI_PNG' // no need for special processing, just ignore
); break;
return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : 'invalid');
} 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;
public static function BMPcompressionOS2Lookup($compression_id) { $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;
static $lookup = array ( default:
0 => 'BI_RGB', $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
1 => 'BI_RLE8', break;
2 => 'BI_RLE4', }
3 => 'Huffman 1D', break;
4 => 'BI_RLE24',
);
return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : 'invalid');
}
public static function FixedPoint2_30($raw_data) { default: // unhandled compression type
$ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
break;
}
}
$binary_string = getid3_lib::BigEndian2Bin($raw_data); return true;
return bindec(substr($binary_string, 0, 2)) + (float)(bindec(substr($binary_string, 2, 30)) / 1073741824); // pow(2, 30) = 1073741824 }
}
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');
}
} }

View file

@ -1,91 +1,181 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.graphic.gif.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing GIF Image files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.graphic.gif.php |
// | Module for analyzing CompuServe GIF graphic files. |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.graphic.gif.php,v 1.2 2006/11/02 10:48:02 ah Exp $
class getid3_gif
class getid3_gif extends getid3_handler
{ {
public function Analyze() { function getid3_gif(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'gif';
$ThisFileInfo['video']['dataformat'] = 'gif';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$GIFheader = fread($fd, 13);
$offset = 0;
$getid3->info['fileformat'] = 'gif'; $ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
$getid3->info['video']['dataformat'] = 'gif'; $offset += 3;
$getid3->info['video']['lossless'] = true;
$getid3->info['video']['pixel_aspect_ratio'] = (float)1;
$getid3->info['gif']['header'] = array (); if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') {
$info_gif_header = &$getid3->info['gif']['header']; $ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"';
unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['gif']);
return false;
}
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
$gif_header = fread($getid3->fp, 13); $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;
// Magic bytes $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width'];
$info_gif_header['raw']['identifier'] = 'GIF'; $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;
}
getid3_lib::ReadSequence('LittleEndian2Int', $info_gif_header['raw'], $gif_header, 3, // if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
array ( // $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']);
'version' => -3, // string // $offset = 0;
'width' => 2, // for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) {
'height' => 2, // $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
'flags' => 1, // $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
'bg_color_index' => 1, // $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
'aspect_ratio' => 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;
//
//
// }
// }
$getid3->info['video']['resolution_x'] = $info_gif_header['raw']['width']; return true;
$getid3->info['video']['resolution_y'] = $info_gif_header['raw']['height']; }
$getid3->info['gif']['version'] = $info_gif_header['raw']['version'];
$info_gif_header['flags']['global_color_table'] = (bool)($info_gif_header['raw']['flags'] & 0x80);
if ($info_gif_header['raw']['flags'] & 0x80) { function GetLSBits($fd, $bits) {
// Number of bits per primary color available to the original image, minus 1 static $bitbuffer = '';
$info_gif_header['bits_per_pixel'] = 3 * ((($info_gif_header['raw']['flags'] & 0x70) >> 4) + 1); while (strlen($bitbuffer) < $bits) {
} else { //echo 'Read another byte: '.ftell($fd).'<BR>';
$info_gif_header['bits_per_pixel'] = 0; $bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
} }
$info_gif_header['flags']['global_color_sorted'] = (bool)($info_gif_header['raw']['flags'] & 0x40); $value = bindec(substr($bitbuffer, 0 - $bits));
if ($info_gif_header['flags']['global_color_table']) { $bitbuffer = substr($bitbuffer, 0, 0 - $bits);
// the number of bytes contained in the Global Color Table. To determine that
// actual size of the color table, raise 2 to [the value of the field + 1]
$info_gif_header['global_color_size'] = pow(2, ($info_gif_header['raw']['flags'] & 0x07) + 1);
$getid3->info['video']['bits_per_sample'] = ($info_gif_header['raw']['flags'] & 0x07) + 1;
} else {
$info_gif_header['global_color_size'] = 0;
}
if ($info_gif_header['raw']['aspect_ratio'] != 0) { return $value;
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 }
$info_gif_header['aspect_ratio'] = ($info_gif_header['raw']['aspect_ratio'] + 15) / 64;
}
return true;
}
} }

View file

@ -0,0 +1,335 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.graphic.jpg.php //
// module for analyzing JPEG Image files //
// dependencies: PHP compiled with --enable-exif (optional) //
// module.tag.xmp.php (optional) //
// ///
/////////////////////////////////////////////////////////////////
class getid3_jpg
{
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;
}
}
}
}
$returnOK = false;
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')) {
//if (function_exists('exif_read_data') && (strpos($imageinfo['APP1'], 'Exif') === 0)) { // suggested fix: http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1055
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();
}
$returnOK = true;
break;
default:
break;
}
$cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL');
foreach ($cast_as_appropriate_keys as $exif_key) {
if (isset($ThisFileInfo['jpg']['exif'][$exif_key])) {
foreach ($ThisFileInfo['jpg']['exif'][$exif_key] as $key => $value) {
$ThisFileInfo['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value);
}
}
}
if (isset($ThisFileInfo['jpg']['exif']['GPS'])) {
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSVersion'])) {
for ($i = 0; $i < 4; $i++) {
$version_subparts[$i] = ord(substr($ThisFileInfo['jpg']['exif']['GPS']['GPSVersion'], $i, 1));
}
$ThisFileInfo['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts);
}
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSDateStamp'])) {
@list($computed_time[5], $computed_time[3], $computed_time[4]) = explode(':', $ThisFileInfo['jpg']['exif']['GPS']['GPSDateStamp']);
if (function_exists('date_default_timezone_set')) {
date_default_timezone_set('UTC');
} else {
ini_set('date.timezone', 'UTC');
}
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp'])) {
foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) {
$computed_time[$key] = getid3_lib::DecimalizeFraction($value);
}
}
$ThisFileInfo['jpg']['exif']['GPS']['computed']['timestamp'] = mktime(@$computed_time[0], @$computed_time[1], @$computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]);
}
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude'])) {
$direction_multiplier = ((@$ThisFileInfo['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S') ? -1 : 1);
foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) {
$computed_latitude[$key] = getid3_lib::DecimalizeFraction($value);
}
$ThisFileInfo['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600));
}
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude']) && is_array($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude'])) {
$direction_multiplier = ((@$ThisFileInfo['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W') ? -1 : 1);
foreach ($ThisFileInfo['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) {
$computed_longitude[$key] = getid3_lib::DecimalizeFraction($value);
}
$ThisFileInfo['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
}
if (isset($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitude'])) {
$direction_multiplier = ((@$ThisFileInfo['jpg']['exif']['GPS']['GPSAltitudeRef'] == chr(1)) ? -1 : 1);
$ThisFileInfo['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($ThisFileInfo['jpg']['exif']['GPS']['GPSAltitude']);
}
}
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, false)) {
if (isset($ThisFileInfo['filenamepath'])) {
$image_xmp = new Image_XMP($ThisFileInfo['filenamepath']);
$xmp_raw = $image_xmp->getAllTags();
foreach ($xmp_raw as $key => $value) {
list($subsection, $tagname) = explode(':', $key);
$ThisFileInfo['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
}
}
}
if (!$returnOK) {
unset($ThisFileInfo['fileformat']);
return false;
}
return true;
}
function CastAsAppropriate($value) {
if (is_array($value)) {
return $value;
} elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
return getid3_lib::DecimalizeFraction($value);
} elseif (preg_match('#^[0-9]+$#', $value)) {
return getid3_lib::CastAsInt($value);
} elseif (preg_match('#^[0-9\.]+$#', $value)) {
return (float) $value;
}
return $value;
}
function IPTCrecordName($iptc_record) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
static $IPTCrecordName = array();
if (empty($IPTCrecordName)) {
$IPTCrecordName = array(
1 => 'IPTCEnvelope',
2 => 'IPTCApplication',
3 => 'IPTCNewsPhoto',
7 => 'IPTCPreObjectData',
8 => 'IPTCObjectData',
9 => 'IPTCPostObjectData',
);
}
return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
}
function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
static $IPTCrecordTagName = array();
if (empty($IPTCrecordTagName)) {
$IPTCrecordTagName = array(
1 => array( // IPTC EnvelopeRecord Tags
0 => 'EnvelopeRecordVersion',
5 => 'Destination',
20 => 'FileFormat',
22 => 'FileVersion',
30 => 'ServiceIdentifier',
40 => 'EnvelopeNumber',
50 => 'ProductID',
60 => 'EnvelopePriority',
70 => 'DateSent',
80 => 'TimeSent',
90 => 'CodedCharacterSet',
100 => 'UniqueObjectName',
120 => 'ARMIdentifier',
122 => 'ARMVersion',
),
2 => array( // IPTC ApplicationRecord Tags
0 => 'ApplicationRecordVersion',
3 => 'ObjectTypeReference',
4 => 'ObjectAttributeReference',
5 => 'ObjectName',
7 => 'EditStatus',
8 => 'EditorialUpdate',
10 => 'Urgency',
12 => 'SubjectReference',
15 => 'Category',
20 => 'SupplementalCategories',
22 => 'FixtureIdentifier',
25 => 'Keywords',
26 => 'ContentLocationCode',
27 => 'ContentLocationName',
30 => 'ReleaseDate',
35 => 'ReleaseTime',
37 => 'ExpirationDate',
38 => 'ExpirationTime',
40 => 'SpecialInstructions',
42 => 'ActionAdvised',
45 => 'ReferenceService',
47 => 'ReferenceDate',
50 => 'ReferenceNumber',
55 => 'DateCreated',
60 => 'TimeCreated',
62 => 'DigitalCreationDate',
63 => 'DigitalCreationTime',
65 => 'OriginatingProgram',
70 => 'ProgramVersion',
75 => 'ObjectCycle',
80 => 'By-line',
85 => 'By-lineTitle',
90 => 'City',
92 => 'Sub-location',
95 => 'Province-State',
100 => 'Country-PrimaryLocationCode',
101 => 'Country-PrimaryLocationName',
103 => 'OriginalTransmissionReference',
105 => 'Headline',
110 => 'Credit',
115 => 'Source',
116 => 'CopyrightNotice',
118 => 'Contact',
120 => 'Caption-Abstract',
121 => 'LocalCaption',
122 => 'Writer-Editor',
125 => 'RasterizedCaption',
130 => 'ImageType',
131 => 'ImageOrientation',
135 => 'LanguageIdentifier',
150 => 'AudioType',
151 => 'AudioSamplingRate',
152 => 'AudioSamplingResolution',
153 => 'AudioDuration',
154 => 'AudioOutcue',
184 => 'JobID',
185 => 'MasterDocumentID',
186 => 'ShortDocumentID',
187 => 'UniqueDocumentID',
188 => 'OwnerID',
200 => 'ObjectPreviewFileFormat',
201 => 'ObjectPreviewFileVersion',
202 => 'ObjectPreviewData',
221 => 'Prefs',
225 => 'ClassifyState',
228 => 'SimilarityIndex',
230 => 'DocumentNotes',
231 => 'DocumentHistory',
232 => 'ExifCameraInfo',
),
3 => array( // IPTC NewsPhoto Tags
0 => 'NewsPhotoVersion',
10 => 'IPTCPictureNumber',
20 => 'IPTCImageWidth',
30 => 'IPTCImageHeight',
40 => 'IPTCPixelWidth',
50 => 'IPTCPixelHeight',
55 => 'SupplementalType',
60 => 'ColorRepresentation',
64 => 'InterchangeColorSpace',
65 => 'ColorSequence',
66 => 'ICC_Profile',
70 => 'ColorCalibrationMatrix',
80 => 'LookupTable',
84 => 'NumIndexEntries',
85 => 'ColorPalette',
86 => 'IPTCBitsPerSample',
90 => 'SampleStructure',
100 => 'ScanningDirection',
102 => 'IPTCImageRotation',
110 => 'DataCompressionMethod',
120 => 'QuantizationMethod',
125 => 'EndPoints',
130 => 'ExcursionTolerance',
135 => 'BitsPerComponent',
140 => 'MaximumDensityRange',
145 => 'GammaCompensatedValue',
),
7 => array( // IPTC PreObjectData Tags
10 => 'SizeMode',
20 => 'MaxSubfileSize',
90 => 'ObjectSizeAnnounced',
95 => 'MaximumObjectSize',
),
8 => array( // IPTC ObjectData Tags
10 => 'SubFile',
),
9 => array( // IPTC PostObjectData Tags
10 => 'ConfirmedObjectSize',
),
);
}
return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
}
}
?>

View file

@ -1,56 +1,129 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.graphic.pcd.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing PhotoCD (PCD) Image files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.graphic.pcd.php |
// | Module for analyzing PhotoCD (PCD) Image files. |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.graphic.pcd.php,v 1.2 2006/11/02 10:48:02 ah Exp $
class getid3_pcd
class getid3_pcd extends getid3_handler
{ {
function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) {
$ThisFileInfo['fileformat'] = 'pcd';
$ThisFileInfo['video']['dataformat'] = 'pcd';
$ThisFileInfo['video']['lossless'] = false;
public function Analyze() { fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET);
$getid3 = $this->getid3; $PCDflags = fread($fd, 1);
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
$getid3->info['fileformat'] = 'pcd';
$getid3->info['video']['dataformat'] = 'pcd';
$getid3->info['video']['lossless'] = false;
fseek($getid3->fp, $getid3->info['avdataoffset'] + 72, SEEK_SET); if ($PCDisVertical) {
$ThisFileInfo['video']['resolution_x'] = 3072;
$ThisFileInfo['video']['resolution_y'] = 2048;
} else {
$ThisFileInfo['video']['resolution_x'] = 2048;
$ThisFileInfo['video']['resolution_y'] = 3072;
}
$pcd_flags = fread($getid3->fp, 1);
$pcd_is_vertical = ((ord($pcd_flags) & 0x01) ? true : false);
if ($pcd_is_vertical) { if ($ExtractData > 3) {
$getid3->info['video']['resolution_x'] = 3072;
$getid3->info['video']['resolution_y'] = 2048;
} else {
$getid3->info['video']['resolution_x'] = 2048;
$getid3->info['video']['resolution_y'] = 3072;
}
} $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']);
}
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,100 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.graphic.svg.php //
// module for analyzing SVG Image files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_svg
{
function getid3_svg(&$fd, &$ThisFileInfo) {
fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$SVGheader = fread($fd, 4096);
if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) {
$ThisFileInfo['svg']['xml']['raw'] = $matches;
}
if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) {
$ThisFileInfo['svg']['doctype']['raw'] = $matches;
}
if (preg_match('#\<svg([^\>]+)\>#i', $SVGheader, $matches)) {
$ThisFileInfo['svg']['svg']['raw'] = $matches;
}
if (isset($ThisFileInfo['svg']['svg']['raw'])) {
$sections_to_fix = array('xml', 'doctype', 'svg');
foreach ($sections_to_fix as $section_to_fix) {
if (!isset($ThisFileInfo['svg'][$section_to_fix])) {
continue;
}
$section_data = array();
while (preg_match('/ "([^"]+)"/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1], $matches)) {
$section_data[] = $matches[1];
$ThisFileInfo['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $ThisFileInfo['svg'][$section_to_fix]['raw'][1]);
}
while (preg_match('/([^\s]+)="([^"]+)"/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1], $matches)) {
$section_data[] = $matches[0];
$ThisFileInfo['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $ThisFileInfo['svg'][$section_to_fix]['raw'][1]);
}
$section_data = array_merge($section_data, preg_split('/[\s,]+/', $ThisFileInfo['svg'][$section_to_fix]['raw'][1]));
foreach ($section_data as $keyvaluepair) {
$keyvaluepair = trim($keyvaluepair);
if ($keyvaluepair) {
@list($key, $value) = explode('=', $keyvaluepair);
$ThisFileInfo['svg'][$section_to_fix]['sections'][$key] = trim($value, '"');
}
}
}
$ThisFileInfo['fileformat'] = 'svg';
$ThisFileInfo['video']['dataformat'] = 'svg';
$ThisFileInfo['video']['lossless'] = true;
//$ThisFileInfo['video']['bits_per_sample'] = 24;
$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
if (@$ThisFileInfo['svg']['svg']['sections']['width']) {
$ThisFileInfo['svg']['width'] = intval($ThisFileInfo['svg']['svg']['sections']['width']);
}
if (@$ThisFileInfo['svg']['svg']['sections']['height']) {
$ThisFileInfo['svg']['height'] = intval($ThisFileInfo['svg']['svg']['sections']['height']);
}
if (@$ThisFileInfo['svg']['svg']['sections']['version']) {
$ThisFileInfo['svg']['version'] = $ThisFileInfo['svg']['svg']['sections']['version'];
}
if (!isset($ThisFileInfo['svg']['version']) && isset($ThisFileInfo['svg']['doctype']['sections'])) {
foreach ($ThisFileInfo['svg']['doctype']['sections'] as $key => $value) {
if (preg_match('#//W3C//DTD SVG ([0-9\.]+)//#i', $key, $matches)) {
$ThisFileInfo['svg']['version'] = $matches[1];
break;
}
}
}
if (@$ThisFileInfo['svg']['width']) {
$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width'];
}
if (@$ThisFileInfo['svg']['height']) {
$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height'];
}
return true;
}
$ThisFileInfo['error'][] = 'Did not find expected <svg> tag';
return false;
}
}
?>

View file

@ -1,214 +1,219 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.archive.tiff.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing TIFF files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.graphic.tiff.php |
// | Module for analyzing TIFF graphic files. |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.graphic.tiff.php,v 1.2 2006/11/02 10:48:02 ah Exp $
class getid3_tiff
class getid3_tiff extends getid3_handler
{ {
public function Analyze() { function getid3_tiff(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
$TIFFheader = fread($fd, 4);
fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); switch (substr($TIFFheader, 0, 2)) {
$tiff_header = fread($getid3->fp, 4); 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;
}
$getid3->info['tiff']['byte_order'] = substr($tiff_header, 0, 2) == 'II' ? 'Intel' : 'Motorola'; $ThisFileInfo['fileformat'] = 'tiff';
$endian2int = substr($tiff_header, 0, 2) == 'II' ? 'LittleEndian2Int' : 'BigEndian2Int'; $ThisFileInfo['video']['dataformat'] = 'tiff';
$ThisFileInfo['video']['lossless'] = true;
$ThisFileInfo['tiff']['ifd'] = array();
$CurrentIFD = array();
$getid3->info['fileformat'] = 'tiff'; $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
$getid3->info['video']['dataformat'] = 'tiff';
$getid3->info['video']['lossless'] = true;
$getid3->info['tiff']['ifd'] = array ();
$current_ifd = array ();
$field_type_byte_length = array (1=>1, 2=>1, 3=>2, 4=>4, 5=>8); $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
$next_ifd_offset = getid3_lib::$endian2int(fread($getid3->fp, 4)); while ($nextIFDoffset > 0) {
while ($next_ifd_offset > 0) { $CurrentIFD['offset'] = $nextIFDoffset;
$current_ifd['offset'] = $next_ifd_offset; fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET);
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
fseek($getid3->fp, $getid3->info['avdataoffset'] + $next_ifd_offset, SEEK_SET); for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
$current_ifd['fieldcount'] = getid3_lib::$endian2int(fread($getid3->fp, 2)); $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);
for ($i = 0; $i < $current_ifd['fieldcount']; $i++) { 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;
// shortcut case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
$current_ifd['fields'][$i] = array (); if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
$current_ifd_fields_i = &$current_ifd['fields'][$i]; $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;
$current_ifd_fields_i['raw']['tag'] = getid3_lib::$endian2int(fread($getid3->fp, 2)); case 3: // SHORT A 16-bit (2-byte) unsigned integer.
$current_ifd_fields_i['raw']['type'] = getid3_lib::$endian2int(fread($getid3->fp, 2)); if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
$current_ifd_fields_i['raw']['length'] = getid3_lib::$endian2int(fread($getid3->fp, 4)); $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']);
$current_ifd_fields_i['raw']['offset'] = fread($getid3->fp, 4); } else {
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
}
break;
switch ($current_ifd_fields_i['raw']['type']) { case 4: // LONG A 32-bit (4-byte) unsigned integer.
case 1: // BYTE An 8-bit unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
if ($current_ifd_fields_i['raw']['length'] <= 4) { $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
$current_ifd_fields_i['value'] = getid3_lib::$endian2int(substr($current_ifd_fields_i['raw']['offset'], 0, 1)); } else {
} else { $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
$current_ifd_fields_i['offset'] = getid3_lib::$endian2int($current_ifd_fields_i['raw']['offset']); }
} break;
break;
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null. case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
if ($current_ifd_fields_i['raw']['length'] <= 4) { break;
$current_ifd_fields_i['value'] = substr($current_ifd_fields_i['raw']['offset'], 3); }
} else { }
$current_ifd_fields_i['offset'] = getid3_lib::$endian2int($current_ifd_fields_i['raw']['offset']);
}
break;
case 3: // SHORT A 16-bit (2-byte) unsigned integer. $ThisFileInfo['tiff']['ifd'][] = $CurrentIFD;
if ($current_ifd_fields_i['raw']['length'] <= 2) { $CurrentIFD = array();
$current_ifd_fields_i['value'] = getid3_lib::$endian2int(substr($current_ifd_fields_i['raw']['offset'], 0, 2)); $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
} else {
$current_ifd_fields_i['offset'] = getid3_lib::$endian2int($current_ifd_fields_i['raw']['offset']);
}
break;
case 4: // LONG A 32-bit (4-byte) unsigned integer. }
if ($current_ifd_fields_i['raw']['length'] <= 1) {
$current_ifd_fields_i['value'] = getid3_lib::$endian2int($current_ifd_fields_i['raw']['offset']);
} else {
$current_ifd_fields_i['offset'] = getid3_lib::$endian2int($current_ifd_fields_i['raw']['offset']);
}
break;
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) {
break; 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']]);
$getid3->info['tiff']['ifd'][] = $current_ifd; }
$current_ifd = array (); break;
$next_ifd_offset = getid3_lib::$endian2int(fread($getid3->fp, 4));
} 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']]);
foreach ($getid3->info['tiff']['ifd'] as $ifd_id => $ifd_array) { }
foreach ($ifd_array['fields'] as $key => $field_array) { break;
switch ($field_array['raw']['tag']) { }
case 256: // ImageWidth switch ($fieldarray['raw']['tag']) {
case 257: // ImageLength case 256: // ImageWidth
case 258: // BitsPerSample $ThisFileInfo['video']['resolution_x'] = $fieldarray['value'];
case 259: // Compression break;
if (!isset($field_array['value'])) {
fseek($getid3->fp, $field_array['offset'], SEEK_SET);
$getid3->info['tiff']['ifd'][$ifd_id]['fields'][$key]['raw']['data'] = fread($getid3->fp, $field_array['raw']['length'] * $field_type_byte_length[$field_array['raw']['type']]);
}
break;
case 270: // ImageDescription case 257: // ImageLength
case 271: // Make $ThisFileInfo['video']['resolution_y'] = $fieldarray['value'];
case 272: // Model break;
case 305: // Software
case 306: // DateTime
case 315: // Artist
case 316: // HostComputer
if (isset($field_array['value'])) {
$getid3->info['tiff']['ifd'][$ifd_id]['fields'][$key]['raw']['data'] = $field_array['value'];
} else {
fseek($getid3->fp, $field_array['offset'], SEEK_SET);
$getid3->info['tiff']['ifd'][$ifd_id]['fields'][$key]['raw']['data'] = fread($getid3->fp, $field_array['raw']['length'] * $field_type_byte_length[$field_array['raw']['type']]);
}
break;
}
switch ($field_array['raw']['tag']) {
case 256: // ImageWidth
$getid3->info['video']['resolution_x'] = $field_array['value'];
break;
case 257: // ImageLength case 258: // BitsPerSample
$getid3->info['video']['resolution_y'] = $field_array['value']; if (isset($fieldarray['value'])) {
break; $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 258: // BitsPerSample case 259: // Compression
if (isset($field_array['value'])) { $ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
$getid3->info['video']['bits_per_sample'] = $field_array['value']; break;
} else {
$getid3->info['video']['bits_per_sample'] = 0;
for ($i = 0; $i < $field_array['raw']['length']; $i++) {
$getid3->info['video']['bits_per_sample'] += getid3_lib::$endian2int(substr($getid3->info['tiff']['ifd'][$ifd_id]['fields'][$key]['raw']['data'], $i * $field_type_byte_length[$field_array['raw']['type']], $field_type_byte_length[$field_array['raw']['type']]));
}
}
break;
case 259: // Compression case 270: // ImageDescription
$getid3->info['video']['codec'] = getid3_tiff::TIFFcompressionMethod($field_array['value']); case 271: // Make
break; 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;
case 270: // ImageDescription default:
case 271: // Make break;
case 272: // Model }
case 305: // Software }
case 306: // DateTime }
case 315: // Artist
case 316: // HostComputer
@$getid3->info['tiff']['comments'][getid3_tiff::TIFFcommentName($field_array['raw']['tag'])][] = $getid3->info['tiff']['ifd'][$ifd_id]['fields'][$key]['raw']['data'];
break;
default: return true;
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;
}
public static function TIFFcompressionMethod($id) { 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.')');
}
static $lookup = array ( function TIFFcommentName($id) {
1 => 'Uncompressed', static $TIFFcommentName = array();
2 => 'Huffman', if (empty($TIFFcommentName)) {
3 => 'Fax - CCITT 3', $TIFFcommentName = array(
5 => 'LZW', 270 => 'imagedescription',
32773 => 'PackBits', 271 => 'make',
); 272 => 'model',
return (isset($lookup[$id]) ? $lookup[$id] : 'unknown/invalid ('.$id.')'); 305 => 'software',
} 306 => 'datetime',
315 => 'artist',
316 => 'hostcomputer',
);
public static function TIFFcommentName($id) { }
return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
static $lookup = array ( }
270 => 'imagedescription',
271 => 'make',
272 => 'model',
305 => 'software',
306 => 'datetime',
315 => 'artist',
316 => 'hostcomputer',
);
return (isset($lookup[$id]) ? $lookup[$id] : 'unknown/invalid ('.$id.')');
}
} }

View file

@ -0,0 +1,305 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.misc.cue.php //
// module for analyzing CUEsheet files //
// dependencies: NONE //
// //
/////////////////////////////////////////////////////////////////
// //
// Module originally written [2009-Mar-25] by //
// Nigel Barnes <ngbarnesØhotmail*com> //
// Minor reformatting and similar small changes to integrate //
// into getID3 by James Heinrich <info@getid3.org> //
// ///
/////////////////////////////////////////////////////////////////
/*
* CueSheet parser by Nigel Barnes.
*
* This is a PHP conversion of CueSharp 0.5 by Wyatt O'Day (wyday.com/cuesharp)
*/
/**
* A CueSheet class used to open and parse cuesheets.
*
*/
class getid3_cue
{
var $cuesheet = array();
function getid3_cue(&$fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'cue';
$this->readCueSheetFilename($ThisFileInfo['filenamepath']);
$ThisFileInfo['cue'] = $this->cuesheet;
return true;
}
function readCueSheetFilename($filename)
{
$filedata = file_get_contents($filename);
return $this->readCueSheet($filedata);
}
/**
* Parses a cue sheet file.
*
* @param string $filename - The filename for the cue sheet to open.
*/
function readCueSheet(&$filedata)
{
$cue_lines = array();
foreach (explode("\n", str_replace("\r", null, $filedata)) as $line)
{
if ( (strlen($line) > 0) && ($line[0] != '#'))
{
$cue_lines[] = trim($line);
}
}
$this->parseCueSheet($cue_lines);
return $this->cuesheet;
}
/**
* Parses the cue sheet array.
*
* @param array $file - The cuesheet as an array of each line.
*/
function parseCueSheet($file)
{
//-1 means still global, all others are track specific
$track_on = -1;
for ($i=0; $i < count($file); $i++)
{
list($key) = explode(' ', strtolower($file[$i]), 2);
switch ($key)
{
case 'catalog':
case 'cdtextfile':
case 'isrc':
case 'performer':
case 'songwriter':
case 'title':
$this->parseString($file[$i], $track_on);
break;
case 'file':
$currentFile = $this->parseFile($file[$i]);
break;
case 'flags':
$this->parseFlags($file[$i], $track_on);
break;
case 'index':
case 'postgap':
case 'pregap':
$this->parseIndex($file[$i], $track_on);
break;
case 'rem':
$this->parseComment($file[$i], $track_on);
break;
case 'track':
$track_on++;
$this->parseTrack($file[$i], $track_on);
if (isset($currentFile)) // if there's a file
{
$this->cuesheet['tracks'][$track_on]['datafile'] = $currentFile;
}
break;
default:
//save discarded junk and place string[] with track it was found in
$this->parseGarbage($file[$i], $track_on);
break;
}
}
}
/**
* Parses the REM command.
*
* @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing.
*/
function parseComment($line, $track_on)
{
@list($comment_REM, $comment_type, $comment_data) = explode(' ', $line, 3);
if (($comment_REM == 'REM') && $comment_type)
{
$comment_type = strtolower($comment_type);
$commment_data = trim($comment_data, ' "');
if ($track_on != -1) {
$this->cuesheet['tracks'][$track_on]['comments'][$comment_type][] = $comment_data;
} else {
$this->cuesheet['comments'][$comment_type][] = $comment_data;
}
}
}
/**
* Parses the FILE command.
*
* @param string $line - The line in the cue file that contains the FILE command.
* @return array - Array of FILENAME and TYPE of file..
*/
function parseFile($line)
{
$line = substr($line, strpos($line, ' ') + 1);
$type = strtolower(substr($line, strrpos($line, ' ')));
//remove type
$line = substr($line, 0, strrpos($line, ' ') - 1);
//if quotes around it, remove them.
$line = trim($line, '"');
return array('filename'=>$line, 'type'=>$type);
}
/**
* Parses the FLAG command.
*
* @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing.
*/
function parseFlags($line, $track_on)
{
if ($track_on != -1)
{
foreach (explode(' ', strtolower($line)) as $type)
{
switch ($type)
{
case 'flags':
// first entry in this line
$this->cuesheet['tracks'][$track_on]['flags'] = array(
'4ch' => false,
'data' => false,
'dcp' => false,
'pre' => false,
'scms' => false,
);
break;
case 'data':
case 'dcp':
case '4ch':
case 'pre':
case 'scms':
$this->cuesheet['tracks'][$track_on]['flags'][$type] = true;
break;
default:
break;
}
}
}
}
/**
* Collect any unidentified data.
*
* @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing.
*/
function parseGarbage($line, $track_on)
{
if ( strlen($line) > 0 )
{
if ($track_on == -1)
{
$this->cuesheet['garbage'][] = $line;
}
else
{
$this->cuesheet['tracks'][$track_on]['garbage'][] = $line;
}
}
}
/**
* Parses the INDEX command of a TRACK.
*
* @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing.
*/
function parseIndex($line, $track_on)
{
$type = strtolower(substr($line, 0, strpos($line, ' ')));
$line = substr($line, strpos($line, ' ') + 1);
if ($type == 'index')
{
//read the index number
$number = intval(substr($line, 0, strpos($line, ' ')));
$line = substr($line, strpos($line, ' ') + 1);
}
//extract the minutes, seconds, and frames
@list($minutes, $seconds, $frames) = explode(':', $line);
switch ($type) {
case 'index':
$this->cuesheet['tracks'][$track_on][$type][$number] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
break;
case 'pregap':
case 'postgap':
$this->cuesheet['tracks'][$track_on][$type] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
break;
}
}
function parseString($line, $track_on)
{
$category = strtolower(substr($line, 0, strpos($line, ' ')));
$line = substr($line, strpos($line, ' ') + 1);
//get rid of the quotes
$line = trim($line, '"');
switch ($category)
{
case 'catalog':
case 'cdtextfile':
case 'isrc':
case 'performer':
case 'songwriter':
case 'title':
if ($track_on == -1)
{
$this->cuesheet[$category] = $line;
}
else
{
$this->cuesheet['tracks'][$track_on][$category] = $line;
}
break;
default:
break;
}
}
/**
* Parses the TRACK command.
*
* @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing.
*/
function parseTrack($line, $track_on)
{
$line = substr($line, strpos($line, ' ') + 1);
$track = ltrim(substr($line, 0, strpos($line, ' ')), '0');
//find the data type.
$datatype = strtolower(substr($line, strpos($line, ' ') + 1));
$this->cuesheet['tracks'][$track_on] = array('track_number'=>$track, 'datatype'=>$datatype);
}
}
?>

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

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

View file

@ -1,450 +1,385 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.misc.iso.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing ISO files //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.misc.iso.php |
// | Module for analyzing ISO files |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.misc.iso.php,v 1.3 2006/11/02 10:48:02 ah Exp $
class getid3_iso
class getid3_iso extends getid3_handler
{ {
public function Analyze() { function getid3_iso($fd, &$ThisFileInfo) {
$ThisFileInfo['fileformat'] = 'iso';
$getid3 = $this->getid3;
for ($i = 16; $i <= 19; $i++) {
$getid3->info['fileformat'] = 'iso'; fseek($fd, 2048 * $i, SEEK_SET);
$ISOheader = fread($fd, 2048);
for ($i = 16; $i <= 19; $i++) { if (substr($ISOheader, 1, 5) == 'CD001') {
fseek($getid3->fp, 2048 * $i, SEEK_SET); switch (ord($ISOheader{0})) {
$iso_header = fread($getid3->fp, 2048); case 1:
if (substr($iso_header, 1, 5) == 'CD001') { $ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
switch (ord($iso_header{0})) { $this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo);
case 1: break;
$getid3->info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
$this->ParsePrimaryVolumeDescriptor($iso_header); case 2:
break; $ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
$this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo);
case 2: break;
$getid3->info['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
$this->ParseSupplementaryVolumeDescriptor($iso_header); default:
break; // skip
break;
default: }
// skip }
break; }
}
} $this->ParsePathTable($fd, $ThisFileInfo);
}
$ThisFileInfo['iso']['files'] = array();
$this->ParsePathTable(); foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) {
$getid3->info['iso']['files'] = array (); $ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo);
foreach ($getid3->info['iso']['path_table']['directories'] as $directory_num => $directory_data) {
$getid3->info['iso']['directories'][$directory_num] = $this->ParseDirectoryRecord($directory_data); }
}
return true;
return true;
} }
function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
private function ParsePrimaryVolumeDescriptor(&$iso_header) { // 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
$getid3 = $this->getid3;
// shortcuts
// ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!! $ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array();
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field $thisfile_iso_primaryVD = &$ThisFileInfo['iso']['primary_volume_descriptor'];
$thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw'];
$getid3->info['iso']['primary_volume_descriptor']['raw'] = array ();
$info_iso_primaryVD = &$getid3->info['iso']['primary_volume_descriptor']; $thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
$info_iso_primaryVD_raw = &$info_iso_primaryVD['raw']; $thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') {
$info_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($iso_header, 0, 1)); $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead';
$info_iso_primaryVD_raw['standard_identifier'] = substr($iso_header, 1, 5); unset($ThisFileInfo['fileformat']);
if ($info_iso_primaryVD_raw['standard_identifier'] != 'CD001') { unset($ThisFileInfo['iso']);
throw new getid3_exception('Expected "CD001" at offset ('.($info_iso_primaryVD['offset'] + 1).'), found "'.$info_iso_primaryVD_raw['standard_identifier'].'" instead'); return false;
} }
getid3_lib::ReadSequence('LittleEndian2Int', $info_iso_primaryVD_raw, $iso_header, 6,
array ( $thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
'volume_descriptor_version' => 1, //$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
'IGNORE-unused_1' => 1, $thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
'system_identifier' => -32, // string $thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
'volume_identifier' => -32, // string //$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
'IGNORE-unused_2' => 8, $thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
'volume_space_size' => 4, //$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
'IGNORE-1' => 4, $thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
'IGNORE-unused_3' => 32, $thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
'volume_set_size' => 2, $thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
'IGNORE-2' => 2, $thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
'volume_sequence_number' => 2, $thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
'IGNORE-3' => 2, $thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
'logical_block_size' => 2, $thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
'IGNORE-4' => 2, $thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
'path_table_size' => 4, $thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
'IGNORE-5' => 4, $thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
'path_table_l_location' => 2, $thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
'IGNORE-6' => 2, $thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
'path_table_l_opt_location' => 2, $thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
'IGNORE-7' => 2, $thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
'path_table_m_location' => 2, $thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
'IGNORE-8' => 2, $thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
'path_table_m_opt_location' => 2, $thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
'IGNORE-9' => 2, $thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
'root_directory_record' => -34, // string $thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
'volume_set_identifier' => -128, // string $thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
'publisher_identifier' => -128, // string $thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
'data_preparer_identifier' => -128, // string //$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
'application_identifier' => -128, // string $thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
'copyright_file_identifier' => -37, // string //$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
'abstract_file_identifier' => -37, // string
'bibliographic_file_identifier' => -37, // string $thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']);
'volume_creation_date_time' => -17, // string $thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
'volume_modification_date_time' => -17, // string $thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
'volume_expiration_date_time' => -17, // string $thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
'volume_effective_date_time' => -17, // string $thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
'file_structure_version' => 1, $thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']);
'IGNORE-unused_4' => 1, $thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
'application_data' => -512 // string $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']);
$info_iso_primaryVD['system_identifier'] = trim($info_iso_primaryVD_raw['system_identifier']); $thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']);
$info_iso_primaryVD['volume_identifier'] = trim($info_iso_primaryVD_raw['volume_identifier']); $thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']);
$info_iso_primaryVD['volume_set_identifier'] = trim($info_iso_primaryVD_raw['volume_set_identifier']);
$info_iso_primaryVD['publisher_identifier'] = trim($info_iso_primaryVD_raw['publisher_identifier']); if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) {
$info_iso_primaryVD['data_preparer_identifier'] = trim($info_iso_primaryVD_raw['data_preparer_identifier']); $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?)';
$info_iso_primaryVD['application_identifier'] = trim($info_iso_primaryVD_raw['application_identifier']); }
$info_iso_primaryVD['copyright_file_identifier'] = trim($info_iso_primaryVD_raw['copyright_file_identifier']);
$info_iso_primaryVD['abstract_file_identifier'] = trim($info_iso_primaryVD_raw['abstract_file_identifier']); return true;
$info_iso_primaryVD['bibliographic_file_identifier'] = trim($info_iso_primaryVD_raw['bibliographic_file_identifier']); }
$info_iso_primaryVD['volume_creation_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_primaryVD_raw['volume_creation_date_time']);
$info_iso_primaryVD['volume_modification_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_primaryVD_raw['volume_modification_date_time']); function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
$info_iso_primaryVD['volume_expiration_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_primaryVD_raw['volume_expiration_date_time']); // ISO integer values are stored Both-Endian format!!
$info_iso_primaryVD['volume_effective_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_primaryVD_raw['volume_effective_date_time']); // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
if (($info_iso_primaryVD_raw['volume_space_size'] * 2048) > $getid3->info['filesize']) { // shortcuts
throw new getid3_exception('Volume Space Size ('.($info_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$getid3->info['filesize'].' bytes) (truncated file?)'); $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array();
} $thisfile_iso_supplementaryVD = &$ThisFileInfo['iso']['supplementary_volume_descriptor'];
$thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw'];
return true;
} $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';
private function ParseSupplementaryVolumeDescriptor(&$iso_header) { unset($ThisFileInfo['fileformat']);
unset($ThisFileInfo['iso']);
$getid3 = $this->getid3; return false;
}
// ISO integer values are stored Both-Endian format!!
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field $thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
//$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
$getid3->info['iso']['supplementary_volume_descriptor']['raw'] = array (); $thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
$info_iso_supplementaryVD = &$getid3->info['iso']['supplementary_volume_descriptor']; $thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
$info_iso_supplementaryVD_raw = &$info_iso_supplementaryVD['raw']; //$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
$thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
$info_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($iso_header, 0, 1)); if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
$info_iso_supplementaryVD_raw['standard_identifier'] = substr($iso_header, 1, 5); // Supplementary Volume Descriptor not used
if ($info_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') { //unset($thisfile_iso_supplementaryVD);
throw new getid3_exception('Expected "CD001" at offset ('.($info_iso_supplementaryVD['offset'] + 1).'), found "'.$info_iso_supplementaryVD_raw['standard_identifier'].'" instead'); //return false;
} }
getid3_lib::ReadSequence('LittleEndian2Int', $info_iso_supplementaryVD_raw, $iso_header, 6, //$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
array ( $thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
'volume_descriptor_version' => 1, $thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
'IGNORE-unused_1' => -1, $thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
'system_identifier' => -32, $thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
'volume_identifier' => -32, $thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
'IGNORE-unused_2' => -8, $thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
'volume_space_size' => 4, $thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
'IGNORE-1' => 4, $thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
'IGNORE-unused_3' => -32, $thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
'volume_set_size' => 2, $thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
'IGNORE-2' => 2, $thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
'volume_sequence_number' => 2, $thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
'IGNORE-3' => 2, $thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
'logical_block_size' => 2, $thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
'IGNORE-4' => 2, $thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
'path_table_size' => 4, $thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
'IGNORE-5' => 4, $thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
'path_table_l_location' => 2, $thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
'IGNORE-6' => 2, $thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
'path_table_l_opt_location' => 2, $thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
'IGNORE-7' => 2, $thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
'path_table_m_location' => 2, //$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
'IGNORE-8' => 2, $thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
'path_table_m_opt_location' => 2, //$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
'IGNORE-9' => 2,
'root_directory_record' => -34, $thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
'volume_set_identifier' => -128, $thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
'publisher_identifier' => -128, $thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
'data_preparer_identifier' => -128, $thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
'application_identifier' => -128, $thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
'copyright_file_identifier' => -37, $thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
'abstract_file_identifier' => -37, $thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
'bibliographic_file_identifier' => -37, $thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
'volume_creation_date_time' => -17, $thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
'volume_modification_date_time' => -17, $thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
'volume_expiration_date_time' => -17, $thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
'volume_effective_date_time' => -17, $thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']);
'file_structure_version' => 1, $thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']);
'IGNORE-unused_4' => 1,
'application_data' => -512 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?)';
); }
$info_iso_supplementaryVD['system_identifier'] = trim($info_iso_supplementaryVD_raw['system_identifier']); return true;
$info_iso_supplementaryVD['volume_identifier'] = trim($info_iso_supplementaryVD_raw['volume_identifier']); }
$info_iso_supplementaryVD['volume_set_identifier'] = trim($info_iso_supplementaryVD_raw['volume_set_identifier']);
$info_iso_supplementaryVD['publisher_identifier'] = trim($info_iso_supplementaryVD_raw['publisher_identifier']);
$info_iso_supplementaryVD['data_preparer_identifier'] = trim($info_iso_supplementaryVD_raw['data_preparer_identifier']); function ParsePathTable($fd, &$ThisFileInfo) {
$info_iso_supplementaryVD['application_identifier'] = trim($info_iso_supplementaryVD_raw['application_identifier']); if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
$info_iso_supplementaryVD['copyright_file_identifier'] = trim($info_iso_supplementaryVD_raw['copyright_file_identifier']); return false;
$info_iso_supplementaryVD['abstract_file_identifier'] = trim($info_iso_supplementaryVD_raw['abstract_file_identifier']); }
$info_iso_supplementaryVD['bibliographic_file_identifier'] = trim($info_iso_supplementaryVD_raw['bibliographic_file_identifier']); if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) {
$PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'];
$info_iso_supplementaryVD['volume_creation_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_supplementaryVD_raw['volume_creation_date_time']); $PathTableSize = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
$info_iso_supplementaryVD['volume_modification_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_supplementaryVD_raw['volume_modification_date_time']); $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
$info_iso_supplementaryVD['volume_expiration_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_supplementaryVD_raw['volume_expiration_date_time']); } else {
$info_iso_supplementaryVD['volume_effective_date_time'] = getid3_iso::ISOtimeText2UNIXtime($info_iso_supplementaryVD_raw['volume_effective_date_time']); $PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'];
$PathTableSize = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size'];
if (($info_iso_supplementaryVD_raw['volume_space_size'] * $info_iso_supplementaryVD_raw['logical_block_size']) > $getid3->info['filesize']) { $TextEncoding = 'ISO-8859-1'; // Latin-1
throw new getid3_exception('Volume Space Size ('.($info_iso_supplementaryVD_raw['volume_space_size'] * $info_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$getid3->info['filesize'].' bytes) (truncated file?)'); }
}
if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) {
return true; $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;
private function ParsePathTable() { fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET);
$ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize);
$getid3 = $this->getid3;
$offset = 0;
if (!isset($getid3->info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($getid3->info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { $pathcounter = 1;
return false; while ($offset < $PathTableSize) {
} // shortcut
if (isset($getid3->info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) { $ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array();
$path_table_location = $getid3->info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']; $thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter];
$path_table_size = $getid3->info['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
$text_encoding = 'UTF-16BE'; // Big-Endian Unicode $thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
} $offset += 1;
else { $thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
$path_table_location = $getid3->info['iso']['primary_volume_descriptor']['raw']['path_table_l_location']; $offset += 1;
$path_table_size = $getid3->info['iso']['primary_volume_descriptor']['raw']['path_table_size']; $thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4));
$text_encoding = 'ISO-8859-1'; // Latin-1 $offset += 4;
} $thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2));
$offset += 2;
if (($path_table_location * 2048) > $getid3->info['filesize']) { $thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']);
throw new getid3_exception('Path Table Location specifies an offset ('.($path_table_location * 2048).') beyond the end-of-file ('.$getid3->info['filesize'].')'); $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']);
$getid3->info['iso']['path_table']['offset'] = $path_table_location * 2048;
fseek($getid3->fp, $getid3->info['iso']['path_table']['offset'], SEEK_SET); $thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048;
$getid3->info['iso']['path_table']['raw'] = fread($getid3->fp, $path_table_size); if ($pathcounter == 1) {
$thisfile_iso_pathtable_directories_current['full_path'] = '/';
$offset = 0; } else {
$pathcounter = 1; $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'].'/';
while ($offset < $path_table_size) { }
$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
$getid3->info['iso']['path_table']['directories'][$pathcounter] = array ();
$info_iso_pathtable_directories_current = &$getid3->info['iso']['path_table']['directories'][$pathcounter]; $pathcounter++;
}
getid3_lib::ReadSequence('LittleEndian2Int', $info_iso_pathtable_directories_current, $getid3->info['iso']['path_table']['raw'], $offset,
array ( return true;
'length' => 1, }
'extended_length' => 1,
'location_logical' => 4,
'parent_directory' => 2, function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) {
) if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) {
); $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
} else {
$info_iso_pathtable_directories_current['name'] = substr($getid3->info['iso']['path_table']['raw'], $offset+8, $info_iso_pathtable_directories_current['length']); $TextEncoding = 'ISO-8859-1'; // Latin-1
}
$offset += 8 + $info_iso_pathtable_directories_current['length'] + ($info_iso_pathtable_directories_current['length'] % 2);
fseek($fd, $directorydata['location_bytes'], SEEK_SET);
$info_iso_pathtable_directories_current['name_ascii'] = $getid3->iconv($text_encoding, $getid3->encoding, $info_iso_pathtable_directories_current['name'], true); $DirectoryRecordData = fread($fd, 1);
$info_iso_pathtable_directories_current['location_bytes'] = $info_iso_pathtable_directories_current['location_logical'] * 2048; while (ord($DirectoryRecordData{0}) > 33) {
if ($pathcounter == 1) {
$info_iso_pathtable_directories_current['full_path'] = '/'; $DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1);
}
else { $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
$info_iso_pathtable_directories_current['full_path'] = $getid3->info['iso']['path_table']['directories'][$info_iso_pathtable_directories_current['parent_directory']]['full_path'].$info_iso_pathtable_directories_current['name_ascii'].'/'; $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
} $ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
$full_path_array[] = $info_iso_pathtable_directories_current['full_path']; $ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
$ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
$pathcounter++; $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));
return true; $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']);
private function ParseDirectoryRecord($directory_data) {
$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;
}
}
$getid3 = $this->getid3;
$text_encoding = isset($getid3->info['iso']['supplementary_volume_descriptor']) ? 'UTF-16BE' : 'ISO-8859-1';
fseek($getid3->fp, $directory_data['location_bytes'], SEEK_SET);
$directory_record_data = fread($getid3->fp, 1);
while (ord($directory_record_data{0}) > 33) {
$directory_record_data .= fread($getid3->fp, ord($directory_record_data{0}) - 1);
$this_directory_record = array ();
$this_directory_record['raw'] = array ();
$this_directory_record_raw = &$this_directory_record['raw'];
getid3_lib::ReadSequence('LittleEndian2Int', $this_directory_record_raw, $directory_record_data, 0,
array (
'length' => 1,
'extended_attribute_length' => 1,
'offset_logical' => 4,
'IGNORE-1' => 4,
'filesize' => 4,
'IGNORE-2' => 4,
'recording_date_time' => -7,
'file_flags' => 1,
'file_unit_size' => 1,
'interleave_gap_size' => 1,
'volume_sequence_number' => 2,
'IGNORE-3' => 2,
'file_identifier_length' => 1,
)
);
$this_directory_record_raw['file_identifier'] = substr($directory_record_data, 33, $this_directory_record_raw['file_identifier_length']);
$this_directory_record['file_identifier_ascii'] = $getid3->iconv($text_encoding, $getid3->encoding, $this_directory_record_raw['file_identifier'], true);
$this_directory_record['filesize'] = $this_directory_record_raw['filesize'];
$this_directory_record['offset_bytes'] = $this_directory_record_raw['offset_logical'] * 2048;
$this_directory_record['file_flags']['hidden'] = (bool)($this_directory_record_raw['file_flags'] & 0x01);
$this_directory_record['file_flags']['directory'] = (bool)($this_directory_record_raw['file_flags'] & 0x02);
$this_directory_record['file_flags']['associated'] = (bool)($this_directory_record_raw['file_flags'] & 0x04);
$this_directory_record['file_flags']['extended'] = (bool)($this_directory_record_raw['file_flags'] & 0x08);
$this_directory_record['file_flags']['permissions'] = (bool)($this_directory_record_raw['file_flags'] & 0x10);
$this_directory_record['file_flags']['multiple'] = (bool)($this_directory_record_raw['file_flags'] & 0x80);
$this_directory_record['recording_timestamp'] = getid3_iso::ISOtime2UNIXtime($this_directory_record_raw['recording_date_time']);
if ($this_directory_record['file_flags']['directory']) {
$this_directory_record['filename'] = $directory_data['full_path'];
}
else {
$this_directory_record['filename'] = $directory_data['full_path'].getid3_iso::ISOstripFilenameVersion($this_directory_record['file_identifier_ascii']);
$getid3->info['iso']['files'] = getid3_iso::array_merge_clobber($getid3->info['iso']['files'], getid3_iso::CreateDeepArray($this_directory_record['filename'], '/', $this_directory_record['filesize']));
}
$directory_record[] = $this_directory_record;
$directory_record_data = fread($getid3->fp, 1);
}
return $directory_record;
}
public static function ISOstripFilenameVersion($iso_filename) {
// convert 'filename.ext;1' to 'filename.ext'
if (!strstr($iso_filename, ';')) {
return $iso_filename;
}
return substr($iso_filename, 0, strpos($iso_filename, ';'));
}
public static function ISOtimeText2UNIXtime($iso_time) {
if (!(int)substr($iso_time, 0, 4)) {
return false;
}
return gmmktime((int)substr($iso_time, 8, 2), (int)substr($iso_time, 10, 2), (int)substr($iso_time, 12, 2), (int)substr($iso_time, 4, 2), (int)substr($iso_time, 6, 2), (int)substr($iso_time, 0, 4));
}
public static function ISOtime2UNIXtime($iso_time) {
// 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)
return gmmktime(ord($iso_time[3]), ord($iso_time[4]), ord($iso_time[5]), ord($iso_time[1]), ord($iso_time[2]), ord($iso_time[0]) + 1900);
}
public static function array_merge_clobber($array1, $array2) {
// written by kcØhireability*com
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if (!is_array($array1) || !is_array($array2)) {
return false;
}
$newarray = $array1;
foreach ($array2 as $key => $val) {
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
$newarray[$key] = getid3_iso::array_merge_clobber($newarray[$key], $val);
} else {
$newarray[$key] = $val;
}
}
return $newarray;
}
public static function CreateDeepArray($array_path, $separator, $value) {
// assigns $value to a nested array path:
// $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt')
// is the same as:
// $foo = array ('path'=>array('to'=>'array('my'=>array('file.txt'))));
// or
// $foo['path']['to']['my'] = 'file.txt';
while ($array_path{0} == $separator) {
$array_path = substr($array_path, 1);
}
if (($pos = strpos($array_path, $separator)) !== false) {
return array (substr($array_path, 0, $pos) => getid3_iso::CreateDeepArray(substr($array_path, $pos + 1), $separator, $value));
}
return array ($array_path => $value);
}
} }

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

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

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

View file

@ -1,312 +1,293 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.tag.apetag.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing APE tags //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.tag.apetag.php |
// | module for analyzing APE tags |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.tag.apetag.php,v 1.5 2006/11/16 14:05:21 ah Exp $
class getid3_apetag
class getid3_apetag extends getid3_handler
{ {
/*
ID3v1_TAG_SIZE = 128;
APETAG_HEADER_SIZE = 32;
LYRICS3_TAG_SIZE = 10;
*/
public $option_override_end_offset = 0; 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';
public function Analyze() { return false;
}
$getid3 = $this->getid3;
$id3v1tagsize = 128;
if ($this->option_override_end_offset == 0) { $apetagheadersize = 32;
$lyrics3tagsize = 10;
fseek($getid3->fp, 0 - 170, SEEK_END); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE
$apetag_footer_id3v1 = fread($getid3->fp, 170); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE if ($overrideendoffset == 0) {
// APE tag found before ID3v1 fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
if (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 160, 8) == 'APETAGEX') { // 160 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
$getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename) - 128; // 128 = ID3v1_TAG_SIZE
} //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
// APE tag found, no ID3v1
elseif (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 32, 8) == 'APETAGEX') { // 32 = APETAG_HEADER_SIZE // APE tag found before ID3v1
$getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename); $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
}
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
} } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
else {
// APE tag found, no ID3v1
fseek($getid3->fp, $this->option_override_end_offset - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
if (fread($getid3->fp, 8) == 'APETAGEX') {
$getid3->info['ape']['tag_offset_end'] = $this->option_override_end_offset; }
}
} else {
}
fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
// APE tag not found if (fread($fd, 8) == 'APETAGEX') {
if (!@$getid3->info['ape']['tag_offset_end']) { $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
return false; }
}
}
// Shortcut if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
$info_ape = &$getid3->info['ape'];
// APE tag not found
// Read and parse footer unset($ThisFileInfo['ape']);
fseek($getid3->fp, $info_ape['tag_offset_end'] - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE return false;
$apetag_footer_data = fread($getid3->fp, 32);
if (!($this->ParseAPEHeaderFooter($apetag_footer_data, $info_ape['footer']))) { }
throw new getid3_exception('Error parsing APE footer at offset '.$info_ape['tag_offset_end']);
} // shortcut
$thisfile_ape = &$ThisFileInfo['ape'];
if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) {
fseek($getid3->fp, $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize'] - 32, SEEK_SET); fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
$info_ape['tag_offset_start'] = ftell($getid3->fp); $APEfooterData = fread($fd, 32);
$apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize'] + 32); if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
} $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
else { return false;
$info_ape['tag_offset_start'] = $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize']; }
fseek($getid3->fp, $info_ape['tag_offset_start'], SEEK_SET);
$apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize']); 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);
$getid3->info['avdataend'] = $info_ape['tag_offset_start']; $thisfile_ape['tag_offset_start'] = ftell($fd);
$APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
if (isset($getid3->info['id3v1']['tag_offset_start']) && ($getid3->info['id3v1']['tag_offset_start'] < $info_ape['tag_offset_end'])) { } else {
$getid3->warning('ID3v1 tag information ignored since it appears to be a false synch in APEtag data'); $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
unset($getid3->info['id3v1']); fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
} $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
}
$offset = 0; $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) {
if (!$this->ParseAPEHeaderFooter(substr($apetag_data, 0, 32), $info_ape['header'])) { if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
throw new getid3_exception('Error parsing APE header at offset '.$info_ape['tag_offset_start']); $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
} unset($ThisFileInfo['id3v1']);
$offset = 32; foreach ($ThisFileInfo['warning'] as $key => $value) {
} if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
unset($ThisFileInfo['warning'][$key]);
// Shortcut sort($ThisFileInfo['warning']);
$getid3->info['replay_gain'] = array (); break;
$info_replaygain = &$getid3->info['replay_gain']; }
}
for ($i = 0; $i < $info_ape['footer']['raw']['tag_items']; $i++) { }
$value_size = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset, 4));
$item_flags = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset + 4, 4)); $offset = 0;
$offset += 8; if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
if (strstr(substr($apetag_data, $offset), "\x00") === false) { $offset += $apetagheadersize;
throw new getid3_exception('Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset '.($info_ape['tag_offset_start'] + $offset)); } else {
} $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
return false;
$item_key_length = strpos($apetag_data, "\x00", $offset) - $offset; }
$item_key = strtolower(substr($apetag_data, $offset, $item_key_length)); }
// Shortcut // shortcut
$info_ape['items'][$item_key] = array (); $ThisFileInfo['replay_gain'] = array();
$info_ape_items_current = &$info_ape['items'][$item_key]; $thisfile_replaygain = &$ThisFileInfo['replay_gain'];
$offset += $item_key_length + 1; // skip 0x00 terminator for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
$info_ape_items_current['data'] = substr($apetag_data, $offset, $value_size); $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
$offset += $value_size; $offset += 4;
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
$offset += 4;
$info_ape_items_current['flags'] = $this->ParseAPEtagFlags($item_flags); 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);
switch ($info_ape_items_current['flags']['item_contents_raw']) { return false;
case 0: // UTF-8 }
case 3: // Locator (URL, filename, etc), UTF-8 encoded $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
$info_ape_items_current['data'] = explode("\x00", trim($info_ape_items_current['data'])); $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
break;
// shortcut
default: // binary data $thisfile_ape['items'][$item_key] = array();
break; $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
}
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
switch (strtolower($item_key)) { $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
case 'replaygain_track_gain': $offset += $value_size;
$info_replaygain['track']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$info_replaygain['track']['originator'] = 'unspecified'; $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
break; switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
case 0: // UTF-8
case 'replaygain_track_peak': case 3: // Locator (URL, filename, etc), UTF-8 encoded
$info_replaygain['track']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
$info_replaygain['track']['originator'] = 'unspecified'; break;
if ($info_replaygain['track']['peak'] <= 0) {
$getid3->warning('ReplayGain Track peak from APEtag appears invalid: '.$info_replaygain['track']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")'); default: // binary data
} break;
break; }
case 'replaygain_album_gain': switch (strtolower($item_key)) {
$info_replaygain['album']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! case 'replaygain_track_gain':
$info_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
break; $thisfile_replaygain['track']['originator'] = 'unspecified';
break;
case 'replaygain_album_peak':
$info_replaygain['album']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero! case 'replaygain_track_peak':
$info_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if ($info_replaygain['album']['peak'] <= 0) { $thisfile_replaygain['track']['originator'] = 'unspecified';
$getid3->warning('ReplayGain Album peak from APEtag appears invalid: '.$info_replaygain['album']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")'); 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; }
break;
case 'mp3gain_undo':
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $info_ape_items_current['data'][0]); case 'replaygain_album_gain':
$info_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$info_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); $thisfile_replaygain['album']['originator'] = 'unspecified';
$info_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); break;
break;
case 'replaygain_album_peak':
case 'mp3gain_minmax': $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $info_ape_items_current['data'][0]); $thisfile_replaygain['album']['originator'] = 'unspecified';
$info_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); if ($thisfile_replaygain['album']['peak'] <= 0) {
$info_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
break; }
break;
case 'mp3gain_album_minmax':
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $info_ape_items_current['data'][0]); case 'mp3gain_undo':
$info_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$info_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
break; $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
case 'tracknumber': break;
foreach ($info_ape_items_current['data'] as $comment) {
$info_ape['comments']['track'][] = $comment; case 'mp3gain_minmax':
} list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
break; $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
default: break;
foreach ($info_ape_items_current['data'] as $comment) {
$info_ape['comments'][strtolower($item_key)][] = $comment; case 'mp3gain_album_minmax':
} list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
break; $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
} $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
break;
}
if (empty($info_replaygain)) { case 'tracknumber':
unset($getid3->info['replay_gain']); if (is_array($thisfile_ape_items_current['data'])) {
} foreach ($thisfile_ape_items_current['data'] as $comment) {
$thisfile_ape['comments']['track'][] = $comment;
return true; }
} }
break;
default:
protected function ParseAPEheaderFooter($data, &$target) { if (is_array($thisfile_ape_items_current['data'])) {
foreach ($thisfile_ape_items_current['data'] as $comment) {
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
}
if (substr($data, 0, 8) != 'APETAGEX') { }
return false; break;
} }
// shortcut }
$target['raw'] = array (); if (empty($thisfile_replaygain)) {
$target_raw = &$target['raw']; unset($ThisFileInfo['replay_gain']);
}
$target_raw['footer_tag'] = 'APETAGEX';
return true;
getid3_lib::ReadSequence("LittleEndian2Int", $target_raw, $data, 8, }
array (
'version' => 4, function parseAPEheaderFooter($APEheaderFooterData) {
'tagsize' => 4, // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
'tag_items' => 4,
'global_flags' => 4 // shortcut
) $headerfooterinfo['raw'] = array();
); $headerfooterinfo_raw = &$headerfooterinfo['raw'];
$target_raw['reserved'] = substr($data, 24, 8);
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
$target['tag_version'] = $target_raw['version'] / 1000; if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
if ($target['tag_version'] >= 2) { return false;
}
$target['flags'] = $this->ParseAPEtagFlags($target_raw['global_flags']); $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));
return true; $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) {
protected function ParseAPEtagFlags($raw_flag_int) { $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
}
// "Note: APE Tags 1.0 do not use any of the APE Tag flags. return $headerfooterinfo;
// All are set to zero on creation and ignored on reading." }
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
function parseAPEtagFlags($rawflagint) {
$target['header'] = (bool) ($raw_flag_int & 0x80000000); // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
$target['footer'] = (bool) ($raw_flag_int & 0x40000000); // All are set to zero on creation and ignored on reading."
$target['this_is_header'] = (bool) ($raw_flag_int & 0x20000000); // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
$target['item_contents_raw'] = ($raw_flag_int & 0x00000006) >> 1; $flags['header'] = (bool) ($rawflagint & 0x80000000);
$target['read_only'] = (bool) ($raw_flag_int & 0x00000001); $flags['footer'] = (bool) ($rawflagint & 0x40000000);
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
$target['item_contents'] = getid3_apetag::APEcontentTypeFlagLookup($target['item_contents_raw']); $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
return $target;
} $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
return $flags;
}
public static function APEcontentTypeFlagLookup($content_type_id) {
function APEcontentTypeFlagLookup($contenttypeid) {
static $lookup = array ( static $APEcontentTypeFlagLookup = array(
0 => 'utf-8', 0 => 'utf-8',
1 => 'binary', 1 => 'binary',
2 => 'external', 2 => 'external',
3 => 'reserved' 3 => 'reserved'
); );
return (isset($lookup[$content_type_id]) ? $lookup[$content_type_id] : 'invalid'); return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
} }
function APEtagItemIsUTF8Lookup($itemkey) {
static $APEtagItemIsUTF8Lookup = array(
public static function APEtagItemIsUTF8Lookup($item_key) { 'title',
'subtitle',
static $lookup = array ( 'artist',
'title', 'album',
'subtitle', 'debut album',
'artist', 'publisher',
'album', 'conductor',
'debut album', 'track',
'publisher', 'composer',
'conductor', 'comment',
'track', 'copyright',
'composer', 'publicationright',
'comment', 'file',
'copyright', 'year',
'publicationright', 'record date',
'file', 'record location',
'year', 'genre',
'record date', 'media',
'record location', 'related',
'genre', 'isrc',
'media', 'abstract',
'related', 'language',
'isrc', 'bibliography'
'abstract', );
'language', return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
'bibliography' }
);
return in_array(strtolower($item_key), $lookup);
}
} }

View file

@ -1,323 +1,359 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // module.tag.id3v1.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing ID3v1 tags //
// +----------------------------------------------------------------------+ // dependencies: NONE //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.tag.id3v1.php |
// | module for analyzing ID3v1 tags |
// | dependencies: NONE |
// +----------------------------------------------------------------------+
//
// $Id: module.tag.id3v1.php,v 1.6 2006/11/16 16:19:52 ah Exp $
class getid3_id3v1
class getid3_id3v1 extends getid3_handler
{ {
public function Analyze() { function getid3_id3v1(&$fd, &$ThisFileInfo) {
$getid3 = $this->getid3; if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
return false;
}
fseek($getid3->fp, -256, SEEK_END); fseek($fd, -256, SEEK_END);
$pre_id3v1 = fread($getid3->fp, 128); $preid3v1 = fread($fd, 128);
$id3v1_tag = fread($getid3->fp, 128); $id3v1tag = fread($fd, 128);
if (substr($id3v1_tag, 0, 3) == 'TAG') { if (substr($id3v1tag, 0, 3) == 'TAG') {
$getid3->info['avdataend'] -= 128; $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
// Shortcut $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
$getid3->info['id3v1'] = array (); $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
$info_id3v1 = &$getid3->info['id3v1']; $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));
$info_id3v1['title'] = getid3_id3v1::cutfield(substr($id3v1_tag, 3, 30)); // If second-last byte of comment field is null and last byte of comment field is non-null
$info_id3v1['artist'] = getid3_id3v1::cutfield(substr($id3v1_tag, 33, 30)); // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
$info_id3v1['album'] = getid3_id3v1::cutfield(substr($id3v1_tag, 63, 30)); if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
$info_id3v1['year'] = getid3_id3v1::cutfield(substr($id3v1_tag, 93, 4)); $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
$info_id3v1['comment'] = substr($id3v1_tag, 97, 30); // can't remove nulls yet, track detection depends on them $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
$info_id3v1['genreid'] = ord(substr($id3v1_tag, 127, 1)); }
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
// 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 $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
if (($id3v1_tag{125} === "\x00") && ($id3v1_tag{126} !== "\x00")) { if (!empty($ParsedID3v1['genre'])) {
$info_id3v1['track'] = ord(substr($info_id3v1['comment'], 29, 1)); unset($ParsedID3v1['genreid']);
$info_id3v1['comment'] = substr($info_id3v1['comment'], 0, 28); }
} if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
$info_id3v1['comment'] = getid3_id3v1::cutfield($info_id3v1['comment']); unset($ParsedID3v1['genre']);
}
$info_id3v1['genre'] = getid3_id3v1::LookupGenreName($info_id3v1['genreid']); foreach ($ParsedID3v1 as $key => $value) {
if (!empty($info_id3v1['genre'])) { $ParsedID3v1['comments'][$key][0] = $value;
unset($info_id3v1['genreid']); }
}
if (empty($info_id3v1['genre']) || (@$info_id3v1['genre'] == 'Unknown')) {
unset($info_id3v1['genre']);
}
foreach ($info_id3v1 as $key => $value) { // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
$key != 'comments' and $info_id3v1['comments'][$key][0] = $value; $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';
}
$info_id3v1['tag_offset_end'] = filesize($getid3->filename); $ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize'];
$info_id3v1['tag_offset_start'] = $info_id3v1['tag_offset_end'] - 128; $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
}
if (substr($pre_id3v1, 0, 3) == 'TAG') { $ThisFileInfo['id3v1'] = $ParsedID3v1;
// 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, 0, 3) == 'TAG') {
if (substr($pre_id3v1, 96, 8) == 'APETAGEX') { // The way iTunes handles tags is, well, brain-damaged.
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch // It completely ignores v1 if ID3v2 is present.
} elseif (substr($pre_id3v1, 119, 6) == 'LYRICS') { // This goes as far as adding a new v1 tag *even if there already is one*
// a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
} else {
// APE and Lyrics3 footers not found - assume double ID3v1
$getid3->warning('Duplicate ID3v1 tag detected - this has been known to happen with iTunes.');
$getid3->info['avdataend'] -= 128;
}
}
return true; // 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;
}
static function cutfield($str) {
return trim(substr($str, 0, strcspn($str, "\x00")));
}
public static function cutfield($str) { static 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',
return trim(substr($str, 0, strcspn($str, "\x00"))); 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';
}
public static function ArrayOfGenres($allow_SCMPX_extended=false) { return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
}
static $lookup = array ( static function LookupGenreName($genreid, $allowSCMPXextended=true) {
0 => 'Blues', switch ($genreid) {
1 => 'Classic Rock', case 'RX':
2 => 'Country', case 'CR':
3 => 'Dance', break;
4 => 'Disco', default:
5 => 'Funk', if (!is_numeric($genreid)) {
6 => 'Grunge', return false;
7 => 'Hip-Hop', }
8 => 'Jazz', $genreid = intval($genreid); // to handle 3 or '3' or '03'
9 => 'Metal', break;
10 => 'New Age', }
11 => 'Oldies', $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
12 => 'Other', return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
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', static function LookupGenreID($genre, $allowSCMPXextended=false) {
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
foreach ($GenreLookup as $key => $value) {
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
return $key;
}
}
return false;
}
'CR' => 'Cover', static function StandardiseID3v1GenreName($OriginalGenre) {
'RX' => 'Remix' if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
); return getid3_id3v1::LookupGenreName($GenreID);
}
return $OriginalGenre;
}
static $lookupSCMPX = array (); function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
if ($allow_SCMPX_extended && empty($lookupSCMPX)) { $ID3v1Tag = 'TAG';
$lookupSCMPX = $lookup; $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
// Extended ID3v1 genres invented by SCMPX $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
// Note that 255 "Japanese Anime" conflicts with standard "Unknown" $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
$lookupSCMPX[240] = 'Sacred'; if (!empty($track) && ($track > 0) && ($track <= 255)) {
$lookupSCMPX[241] = 'Northern Europe'; $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
$lookupSCMPX[242] = 'Irish & Scottish'; $ID3v1Tag .= "\x00";
$lookupSCMPX[243] = 'Scotland'; if (gettype($track) == 'string') {
$lookupSCMPX[244] = 'Ethnic Europe'; $track = (int) $track;
$lookupSCMPX[245] = 'Enka'; }
$lookupSCMPX[246] = 'Children\'s Song'; $ID3v1Tag .= chr($track);
$lookupSCMPX[247] = 'Japanese Sky'; } else {
$lookupSCMPX[248] = 'Japanese Heavy Rock'; $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$lookupSCMPX[249] = 'Japanese Doom Rock'; }
$lookupSCMPX[250] = 'Japanese J-POP'; if (($genreid < 0) || ($genreid > 147)) {
$lookupSCMPX[251] = 'Japanese Seiyu'; $genreid = 255; // 'unknown' genre
$lookupSCMPX[252] = 'Japanese Ambient Techno'; }
$lookupSCMPX[253] = 'Japanese Moemoe'; switch (gettype($genreid)) {
$lookupSCMPX[254] = 'Japanese Tokusatsu'; case 'string':
//$lookupSCMPX[255] = 'Japanese Anime'; case 'integer':
} $ID3v1Tag .= chr(intval($genreid));
break;
default:
$ID3v1Tag .= chr(255); // 'unknown' genre
break;
}
return ($allow_SCMPX_extended ? $lookupSCMPX : $lookup); return $ID3v1Tag;
} }
public static function LookupGenreName($genre_id, $allow_SCMPX_extended=true) {
switch ($genre_id) {
case 'RX':
case 'CR':
break;
default:
$genre_id = intval($genre_id); // to handle 3 or '3' or '03'
break;
}
$lookup = getid3_id3v1::ArrayOfGenres($allow_SCMPX_extended);
return (isset($lookup[$genre_id]) ? $lookup[$genre_id] : false);
}
public static function LookupGenreID($genre, $allow_SCMPX_extended=false) {
$lookup = getid3_id3v1::ArrayOfGenres($allow_SCMPX_extended);
$lower_case_no_space_search_term = strtolower(str_replace(' ', '', $genre));
foreach ($lookup as $key => $value) {
foreach ($lookup as $key => $value) {
if (strtolower(str_replace(' ', '', $value)) == $lower_case_no_space_search_term) {
return $key;
}
}
return false;
}
return (isset($lookup[$genre_id]) ? $lookup[$genre_id] : false);
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,270 +1,281 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | /// //
// | available through the world-wide-web at the following url: | // module.tag.lyrics3.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for analyzing Lyrics3 tags //
// +----------------------------------------------------------------------+ // dependencies: module.tag.apetag.php (optional) //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | module.tag.lyrics3.php |
// | module for analyzing Lyrics3 tags |
// | dependencies: module.tag.apetag.php (optional) |
// +----------------------------------------------------------------------+
//
// $Id: module.tag.lyrics3.php,v 1.5 2006/11/16 22:04:23 ah Exp $
class getid3_lyrics3 extends getid3_handler class getid3_lyrics3
{ {
public function Analyze() { function getid3_lyrics3(&$fd, &$ThisFileInfo) {
// http://www.volweb.cz/str/tags.htm
$getid3 = $this->getid3; if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
return false;
}
fseek($getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size] fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size]
$lyrics3_id3v1 = fread($getid3->fp, 128 + 9 + 6); $lyrics3_id3v1 = fread($fd, 128 + 9 + 6);
$lyrics3_lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
$lyrics3_end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
$id3v1_tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
// Lyrics3v1, ID3v1, no APE if ($lyrics3end == 'LYRICSEND') {
if ($lyrics3_end == 'LYRICSEND') { // Lyrics3v1, ID3v1, no APE
$lyrics3_size = 5100; $lyrics3size = 5100;
$lyrics3_offset = filesize($getid3->filename) - 128 - $lyrics3_size; $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
$lyrics3_version = 1; $lyrics3version = 1;
}
// Lyrics3v2, ID3v1, no APE } elseif ($lyrics3end == 'LYRICS200') {
elseif ($lyrics3_end == 'LYRICS200') { // Lyrics3v2, ID3v1, no APE
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
$lyrics3_size = $lyrics3_lsz + 6 + strlen('LYRICS200'); $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
$lyrics3_offset = filesize($getid3->filename) - 128 - $lyrics3_size; $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
$lyrics3_version = 2; $lyrics3version = 2;
}
// Lyrics3v1, no ID3v1, no APE } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
elseif (substr(strrev($lyrics3_id3v1), 0, 9) == 'DNESCIRYL') { // strrev('LYRICSEND') = 'DNESCIRYL' // Lyrics3v1, no ID3v1, no APE
$lyrics3_size = 5100; $lyrics3size = 5100;
$lyrics3_offset = filesize($getid3->filename) - $lyrics3_size; $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
$lyrics3_version = 1; $lyrics3version = 1;
$lyrics3_offset = filesize($getid3->filename) - $lyrics3_size; $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size;
}
// Lyrics3v2, no ID3v1, no APE } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
elseif (substr(strrev($lyrics3_id3v1), 0, 9) == '002SCIRYL') { // strrev('LYRICS200') = '002SCIRYL'
$lyrics3_size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 15; // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' // 15 = 6 + strlen('LYRICS200') // Lyrics3v2, no ID3v1, no APE
$lyrics3_offset = filesize($getid3->filename) - $lyrics3_size;
$lyrics3_version = 2;
}
elseif (isset($getid3->info['ape']['tag_offset_start']) && ($getid3->info['ape']['tag_offset_start'] > 15)) { $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;
fseek($getid3->fp, $getid3->info['ape']['tag_offset_start'] - 15, SEEK_SET); } else {
$lyrics3_lsz = fread($getid3->fp, 6);
$lyrics3_end = fread($getid3->fp, 9); 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;
}
// Lyrics3v1, APE, maybe ID3v1 if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) {
if ($lyrics3_end == 'LYRICSEND') { $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;
}
}
}
$lyrics3_size = 5100; $ThisFileInfo['lyrics3'] = $ParsedLyrics3;
$lyrics3_offset = $getid3->info['ape']['tag_offset_start'] - $lyrics3_size;
$getid3->info['avdataend'] = $lyrics3_offset;
$lyrics3_version = 1;
$getid3->warning('APE tag located after Lyrics3, will probably break Lyrics3 compatability');
}
return true;
}
// Lyrics3v2, APE, maybe ID3v1 function Lyrics3Timestamp2Seconds($rawtimestamp) {
elseif ($lyrics3_end == 'LYRICS200') { if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
return (int) (($regs[1] * 60) + $regs[2]);
}
return false;
}
$lyrics3_size = $lyrics3_lsz + 15; // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
$lyrics3_offset = $getid3->info['ape']['tag_offset_start'] - $lyrics3_size; $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
$lyrics3_version = 2; foreach ($lyricsarray as $key => $lyricline) {
$getid3->warning('APE tag located after Lyrics3, will probably break Lyrics3 compatability'); $regs = array();
unset($thislinetimestamps);
while (preg_match('#^(\\[[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') {
//// GetLyrics3Data() return false;
}
return null;
if (isset($lyrics3_offset)) { }
$getid3->info['avdataend'] = $lyrics3_offset;
if ($lyrics3_size <= 0) {
return false;
}
fseek($getid3->fp, $lyrics3_offset, SEEK_SET);
$raw_data = fread($getid3->fp, $lyrics3_size);
if (substr($raw_data, 0, 11) != 'LYRICSBEGIN') {
if (strpos($raw_data, 'LYRICSBEGIN') !== false) {
$getid3->warning('"LYRICSBEGIN" expected at '.$lyrics3_offset.' but actually found at '.($lyrics3_offset + strpos($raw_data, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$lyrics3_version);
$getid3->info['avdataend'] = $lyrics3_offset + strpos($raw_data, 'LYRICSBEGIN');
$parsed_lyrics3['tag_offset_start'] = $getid3->info['avdataend'];
$raw_data = substr($raw_data, strpos($raw_data, 'LYRICSBEGIN'));
$lyrics3_size = strlen($raw_data);
}
else {
throw new getid3_exception('"LYRICSBEGIN" expected at '.$lyrics3_offset.' but found "'.substr($raw_data, 0, 11).'" instead.');
}
}
$parsed_lyrics3['raw']['lyrics3version'] = $lyrics3_version;
$parsed_lyrics3['raw']['lyrics3tagsize'] = $lyrics3_size;
$parsed_lyrics3['tag_offset_start'] = $lyrics3_offset;
$parsed_lyrics3['tag_offset_end'] = $lyrics3_offset + $lyrics3_size;
switch ($lyrics3_version) {
case 1:
if (substr($raw_data, strlen($raw_data) - 9, 9) == 'LYRICSEND') {
$parsed_lyrics3['raw']['LYR'] = trim(substr($raw_data, 11, strlen($raw_data) - 11 - 9));
getid3_lyrics3::Lyrics3LyricsTimestampParse($parsed_lyrics3);
}
else {
throw new getid3_exception('"LYRICSEND" expected at '.(ftell($getid3->fp) - 11 + $lyrics3_size - 9).' but found "'.substr($raw_data, strlen($raw_data) - 9, 9).'" instead.');
}
break;
case 2:
if (substr($raw_data, strlen($raw_data) - 9, 9) == 'LYRICS200') {
$parsed_lyrics3['raw']['unparsed'] = substr($raw_data, 11, strlen($raw_data) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
$raw_data = $parsed_lyrics3['raw']['unparsed'];
while (strlen($raw_data) > 0) {
$fieldname = substr($raw_data, 0, 3);
$fieldsize = (int)substr($raw_data, 3, 5);
$parsed_lyrics3['raw'][$fieldname] = substr($raw_data, 8, $fieldsize);
$raw_data = substr($raw_data, 3 + 5 + $fieldsize);
}
if (isset($parsed_lyrics3['raw']['IND'])) {
$i = 0;
foreach (array ('lyrics', 'timestamps', 'inhibitrandom') as $flagname) {
if (strlen($parsed_lyrics3['raw']['IND']) > ++$i) {
$parsed_lyrics3['flags'][$flagname] = getid3_lyrics3::IntString2Bool(substr($parsed_lyrics3['raw']['IND'], $i, 1));
}
}
}
foreach (array ('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author') as $key => $value) {
if (isset($parsed_lyrics3['raw'][$key])) {
$parsed_lyrics3['comments'][$value][] = trim($parsed_lyrics3['raw'][$key]);
}
}
if (isset($parsed_lyrics3['raw']['IMG'])) {
foreach (explode("\r\n", $parsed_lyrics3['raw']['IMG']) as $key => $image_string) {
if (strpos($image_string, '||') !== false) {
$imagearray = explode('||', $image_string);
$parsed_lyrics3['images'][$key]['filename'] = @$imagearray[0];
$parsed_lyrics3['images'][$key]['description'] = @$imagearray[1];
$parsed_lyrics3['images'][$key]['timestamp'] = getid3_lyrics3::Lyrics3Timestamp2Seconds(@$imagearray[2]);
}
}
}
if (isset($parsed_lyrics3['raw']['LYR'])) {
getid3_lyrics3::Lyrics3LyricsTimestampParse($parsed_lyrics3);
}
}
else {
throw new getid3_exception('"LYRICS200" expected at '.(ftell($getid3->fp) - 11 + $lyrics3_size - 9).' but found "'.substr($raw_data, strlen($raw_data) - 9, 9).'" instead.');
}
break;
default:
throw new getid3_exception('Cannot process Lyrics3 version '.$lyrics3_version.' (only v1 and v2)');
}
if (isset($getid3->info['id3v1']['tag_offset_start']) && ($getid3->info['id3v1']['tag_offset_start'] < $parsed_lyrics3['tag_offset_end'])) {
$getid3->warning('ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data');
unset($getid3->info['id3v1']);
}
$getid3->info['lyrics3'] = $parsed_lyrics3;
// Check for APE tag after lyrics3
if (!@$getid3->info['ape'] && $getid3->option_tag_apetag && class_exists('getid3_apetag')) {
$apetag = new getid3_apetag($getid3);
$apetag->option_override_end_offset = $getid3->info['lyrics3']['tag_offset_start'];
$apetag->Analyze();
}
}
return true;
}
public static function Lyrics3Timestamp2Seconds($rawtimestamp) {
if (preg_match('/^\\[([0-9]{2}):([0-9]{2})\\]$/', $rawtimestamp, $regs)) {
return (int)(($regs[1] * 60) + $regs[2]);
}
return false;
}
public static function Lyrics3LyricsTimestampParse(&$lyrics3_data) {
$lyrics_array = explode("\r\n", $lyrics3_data['raw']['LYR']);
foreach ($lyrics_array as $key => $lyric_line) {
while (preg_match('/^(\\[[0-9]{2}:[0-9]{2}\\])/', $lyric_line, $regs)) {
$this_line_timestamps[] = getid3_lyrics3::Lyrics3Timestamp2Seconds($regs[0]);
$lyric_line = str_replace($regs[0], '', $lyric_line);
}
$no_timestamp_lyrics_array[$key] = $lyric_line;
if (@is_array($this_line_timestamps)) {
sort($this_line_timestamps);
foreach ($this_line_timestamps as $timestampkey => $timestamp) {
if (isset($lyrics3_data['synchedlyrics'][$timestamp])) {
// timestamps only have a 1-second resolution, it's possible that multiple lines
// could have the same timestamp, if so, append
$lyrics3_data['synchedlyrics'][$timestamp] .= "\r\n".$lyric_line;
} else {
$lyrics3_data['synchedlyrics'][$timestamp] = $lyric_line;
}
}
}
unset($this_line_timestamps);
$regs = array ();
}
$lyrics3_data['unsynchedlyrics'] = implode("\r\n", $no_timestamp_lyrics_array);
if (isset($lyrics3_data['synchedlyrics']) && is_array($lyrics3_data['synchedlyrics'])) {
ksort($lyrics3_data['synchedlyrics']);
}
return true;
}
public static function IntString2Bool($char) {
return $char == '1' ? true : ($char == '0' ? false : null);
}
} }

View file

@ -0,0 +1,770 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.tag.xmp.php //
// module for analyzing XMP metadata (e.g. in JPEG files) //
// dependencies: NONE //
// //
/////////////////////////////////////////////////////////////////
// //
// Module originally written [2009-Mar-26] by //
// Nigel Barnes <ngbarnesØhotmail*com> //
// Bundled into getID3 with permission //
// called by getID3 in module.graphic.jpg.php //
// ///
/////////////////////////////////////////////////////////////////
/**************************************************************************************************
* SWISScenter Source Nigel Barnes
*
* Provides functions for reading information from the 'APP1' Extensible Metadata
* Platform (XMP) segment of JPEG format files.
* This XMP segment is XML based and contains the Resource Description Framework (RDF)
* data, which itself can contain the Dublin Core Metadata Initiative (DCMI) information.
*
* This code uses segments from the JPEG Metadata Toolkit project by Evan Hunter.
*************************************************************************************************/
class Image_XMP
{
/**
* @var string
* The name of the image file that contains the XMP fields to extract and modify.
* @see Image_XMP()
*/
var $_sFilename = null;
/**
* @var array
* The XMP fields that were extracted from the image or updated by this class.
* @see getAllTags()
*/
var $_aXMP = array();
/**
* @var boolean
* True if an APP1 segment was found to contain XMP metadata.
* @see isValid()
*/
var $_bXMPParse = false;
/**
* Returns the status of XMP parsing during instantiation
*
* You'll normally want to call this method before trying to get XMP fields.
*
* @return boolean
* Returns true if an APP1 segment was found to contain XMP metadata.
*/
function isValid()
{
return $this->_bXMPParse;
}
/**
* Get a copy of all XMP tags extracted from the image
*
* @return array - An array of XMP fields as it extracted by the XMPparse() function
*/
function getAllTags()
{
return $this->_aXMP;
}
/**
* Reads all the JPEG header segments from an JPEG image file into an array
*
* @param string $filename - the filename of the JPEG file to read
* @return array $headerdata - Array of JPEG header segments
* @return boolean FALSE - if headers could not be read
*/
function _get_jpeg_header_data($filename)
{
// prevent refresh from aborting file operations and hosing file
ignore_user_abort(true);
// Attempt to open the jpeg file - the at symbol supresses the error message about
// not being able to open files. The file_exists would have been used, but it
// does not work with files fetched over http or ftp.
$filehnd = @fopen($filename, 'rb');
// Check if the file opened successfully
if (!$filehnd)
{
// Could't open the file - exit
echo '<p>Could not open file '.htmlentities($filename).'</p>'."\n";
return false;
}
// Read the first two characters
$data = fread($filehnd, 2);
// Check that the first two characters are 0xFF 0xD8 (SOI - Start of image)
if ($data != "\xFF\xD8")
{
// No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
echo '<p>This probably is not a JPEG file</p>'."\n";
fclose($filehnd);
return false;
}
// Read the third character
$data = fread($filehnd, 2);
// Check that the third character is 0xFF (Start of first segment header)
if ($data{0} != "\xFF")
{
// NO FF found - close file and return - JPEG is probably corrupted
fclose($filehnd);
return false;
}
// Flag that we havent yet hit the compressed image data
$hit_compressed_image_data = false;
// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
// 2) we have hit the compressed image data (no more headers are allowed after data)
// 3) or end of file is hit
while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
{
// Found a segment to look at.
// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
{
// Segment isn't a Restart marker
// Read the next two bytes (size)
$sizestr = fread($filehnd, 2);
// convert the size bytes to an integer
$decodedsize = unpack('nsize', $sizestr);
// Save the start position of the data
$segdatastart = ftell($filehnd);
// Read the segment data with length indicated by the previously read size
$segdata = fread($filehnd, $decodedsize['size'] - 2);
// Store the segment information in the output array
$headerdata[] = array(
'SegType' => ord($data{1}),
'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
'SegDataStart' => $segdatastart,
'SegData' => $segdata,
);
}
// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
if ($data{1} == "\xDA")
{
// Flag that we have hit the compressed image data - exit loop as no more headers available.
$hit_compressed_image_data = true;
}
else
{
// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
$data = fread($filehnd, 2);
// Check that the first byte of the two is 0xFF as it should be for a marker
if ($data{0} != "\xFF")
{
// NO FF found - close file and return - JPEG is probably corrupted
fclose($filehnd);
return false;
}
}
}
// Close File
fclose($filehnd);
// Alow the user to abort from now on
ignore_user_abort(false);
// Return the header data retrieved
return $headerdata;
}
/**
* Retrieves XMP information from an APP1 JPEG segment and returns the raw XML text as a string.
*
* @param string $filename - the filename of the JPEG file to read
* @return string $xmp_data - the string of raw XML text
* @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured
*/
function _get_XMP_text($filename)
{
//Get JPEG header data
$jpeg_header_data = $this->_get_jpeg_header_data($filename);
//Cycle through the header segments
for ($i = 0; $i < count($jpeg_header_data); $i++)
{
// If we find an APP1 header,
if (strcmp($jpeg_header_data[$i]['SegName'], 'APP1') == 0)
{
// And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
if (strncmp($jpeg_header_data[$i]['SegData'], 'http://ns.adobe.com/xap/1.0/'."\x00", 29) == 0)
{
// Found a XMP/RDF block
// Return the XMP text
$xmp_data = substr($jpeg_header_data[$i]['SegData'], 29);
return $xmp_data;
}
}
}
return false;
}
/**
* Parses a string containing XMP data (XML), and returns an array
* which contains all the XMP (XML) information.
*
* @param string $xml_text - a string containing the XMP data (XML) to be parsed
* @return array $xmp_array - an array containing all xmp details retrieved.
* @return boolean FALSE - couldn't parse the XMP data
*/
function read_XMP_array_from_text($xmltext)
{
// Check if there actually is any text to parse
if (trim($xmltext) == '')
{
return false;
}
// Create an instance of a xml parser to parse the XML text
$xml_parser = xml_parser_create('UTF-8');
// Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10
// We would like to remove unneccessary white space, but this will also
// remove things like newlines (&#xA;) in the XML values, so white space
// will have to be removed later
if (xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 0) == false)
{
// Error setting case folding - destroy the parser and return
xml_parser_free($xml_parser);
return false;
}
// to use XML code correctly we have to turn case folding
// (uppercasing) off. XML is case sensitive and upper
// casing is in reality XML standards violation
if (xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0) == false)
{
// Error setting case folding - destroy the parser and return
xml_parser_free($xml_parser);
return false;
}
// Parse the XML text into a array structure
if (xml_parse_into_struct($xml_parser, $xmltext, $values, $tags) == 0)
{
// Error Parsing XML - destroy the parser and return
xml_parser_free($xml_parser);
return false;
}
// Destroy the xml parser
xml_parser_free($xml_parser);
// Clear the output array
$xmp_array = array();
// The XMP data has now been parsed into an array ...
// Cycle through each of the array elements
$current_property = ''; // current property being processed
$container_index = -1; // -1 = no container open, otherwise index of container content
foreach ($values as $xml_elem)
{
// Syntax and Class names
switch ($xml_elem['tag'])
{
case 'x:xmpmeta':
// only defined attribute is x:xmptk written by Adobe XMP Toolkit; value is the version of the toolkit
break;
case 'rdf:RDF':
// required element immediately within x:xmpmeta; no data here
break;
case 'rdf:Description':
switch ($xml_elem['type'])
{
case 'open':
case 'complete':
if (array_key_exists('attributes', $xml_elem))
{
// rdf:Description may contain wanted attributes
foreach (array_keys($xml_elem['attributes']) as $key)
{
// Check whether we want this details from this attribute
if (in_array($key, $GLOBALS['XMP_tag_captions']))
{
// Attribute wanted
$xmp_array[$key] = $xml_elem['attributes'][$key];
}
}
}
case 'cdata':
case 'close':
break;
}
case 'rdf:ID':
case 'rdf:nodeID':
// Attributes are ignored
break;
case 'rdf:li':
// Property member
if ($xml_elem['type'] == 'complete')
{
if (array_key_exists('attributes', $xml_elem))
{
// If Lang Alt (language alternatives) then ensure we take the default language
if ($xml_elem['attributes']['xml:lang'] != 'x-default')
{
break;
}
}
if ($current_property != '')
{
$xmp_array[$current_property][$container_index] = $xml_elem['value'];
$container_index += 1;
}
//else unidentified attribute!!
}
break;
case 'rdf:Seq':
case 'rdf:Bag':
case 'rdf:Alt':
// Container found
switch ($xml_elem['type'])
{
case 'open':
$container_index = 0;
break;
case 'close':
$container_index = -1;
break;
case 'cdata':
break;
}
break;
default:
// Check whether we want the details from this attribute
if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
{
switch ($xml_elem['type'])
{
case 'open':
// open current element
$current_property = $xml_elem['tag'];
break;
case 'close':
// close current element
$current_property = '';
break;
case 'complete':
// store attribute value
$xmp_array[$xml_elem['tag']] = @$xml_elem['value'];
break;
case 'cdata':
// ignore
break;
}
}
break;
}
}
return $xmp_array;
}
/**
* Constructor
*
* @param string - Name of the image file to access and extract XMP information from.
*/
function Image_XMP($sFilename)
{
$this->_sFilename = $sFilename;
if (is_file($this->_sFilename))
{
// Get XMP data
$xmp_data = $this->_get_XMP_text($sFilename);
if ($xmp_data)
{
$this->_aXMP = $this->read_XMP_array_from_text($xmp_data);
$this->_bXMPParse = true;
}
}
}
}
/**
* Global Variable: XMP_tag_captions
*
* The Property names of all known XMP fields.
* Note: this is a full list with unrequired properties commented out.
*/
$GLOBALS['XMP_tag_captions'] = array(
// IPTC Core
'Iptc4xmpCore:CiAdrCity',
'Iptc4xmpCore:CiAdrCtry',
'Iptc4xmpCore:CiAdrExtadr',
'Iptc4xmpCore:CiAdrPcode',
'Iptc4xmpCore:CiAdrRegion',
'Iptc4xmpCore:CiEmailWork',
'Iptc4xmpCore:CiTelWork',
'Iptc4xmpCore:CiUrlWork',
'Iptc4xmpCore:CountryCode',
'Iptc4xmpCore:CreatorContactInfo',
'Iptc4xmpCore:IntellectualGenre',
'Iptc4xmpCore:Location',
'Iptc4xmpCore:Scene',
'Iptc4xmpCore:SubjectCode',
// Dublin Core Schema
'dc:contributor',
'dc:coverage',
'dc:creator',
'dc:date',
'dc:description',
'dc:format',
'dc:identifier',
'dc:language',
'dc:publisher',
'dc:relation',
'dc:rights',
'dc:source',
'dc:subject',
'dc:title',
'dc:type',
// XMP Basic Schema
'xmp:Advisory',
'xmp:BaseURL',
'xmp:CreateDate',
'xmp:CreatorTool',
'xmp:Identifier',
'xmp:Label',
'xmp:MetadataDate',
'xmp:ModifyDate',
'xmp:Nickname',
'xmp:Rating',
'xmp:Thumbnails',
'xmpidq:Scheme',
// XMP Rights Management Schema
'xmpRights:Certificate',
'xmpRights:Marked',
'xmpRights:Owner',
'xmpRights:UsageTerms',
'xmpRights:WebStatement',
// These are not in spec but Photoshop CS seems to use them
'xap:Advisory',
'xap:BaseURL',
'xap:CreateDate',
'xap:CreatorTool',
'xap:Identifier',
'xap:MetadataDate',
'xap:ModifyDate',
'xap:Nickname',
'xap:Rating',
'xap:Thumbnails',
'xapidq:Scheme',
'xapRights:Certificate',
'xapRights:Copyright',
'xapRights:Marked',
'xapRights:Owner',
'xapRights:UsageTerms',
'xapRights:WebStatement',
// XMP Media Management Schema
'xapMM:DerivedFrom',
'xapMM:DocumentID',
'xapMM:History',
'xapMM:InstanceID',
'xapMM:ManagedFrom',
'xapMM:Manager',
'xapMM:ManageTo',
'xapMM:ManageUI',
'xapMM:ManagerVariant',
'xapMM:RenditionClass',
'xapMM:RenditionParams',
'xapMM:VersionID',
'xapMM:Versions',
'xapMM:LastURL',
'xapMM:RenditionOf',
'xapMM:SaveID',
// XMP Basic Job Ticket Schema
'xapBJ:JobRef',
// XMP Paged-Text Schema
'xmpTPg:MaxPageSize',
'xmpTPg:NPages',
'xmpTPg:Fonts',
'xmpTPg:Colorants',
'xmpTPg:PlateNames',
// Adobe PDF Schema
'pdf:Keywords',
'pdf:PDFVersion',
'pdf:Producer',
// Photoshop Schema
'photoshop:AuthorsPosition',
'photoshop:CaptionWriter',
'photoshop:Category',
'photoshop:City',
'photoshop:Country',
'photoshop:Credit',
'photoshop:DateCreated',
'photoshop:Headline',
'photoshop:History',
// Not in XMP spec
'photoshop:Instructions',
'photoshop:Source',
'photoshop:State',
'photoshop:SupplementalCategories',
'photoshop:TransmissionReference',
'photoshop:Urgency',
// EXIF Schemas
'tiff:ImageWidth',
'tiff:ImageLength',
'tiff:BitsPerSample',
'tiff:Compression',
'tiff:PhotometricInterpretation',
'tiff:Orientation',
'tiff:SamplesPerPixel',
'tiff:PlanarConfiguration',
'tiff:YCbCrSubSampling',
'tiff:YCbCrPositioning',
'tiff:XResolution',
'tiff:YResolution',
'tiff:ResolutionUnit',
'tiff:TransferFunction',
'tiff:WhitePoint',
'tiff:PrimaryChromaticities',
'tiff:YCbCrCoefficients',
'tiff:ReferenceBlackWhite',
'tiff:DateTime',
'tiff:ImageDescription',
'tiff:Make',
'tiff:Model',
'tiff:Software',
'tiff:Artist',
'tiff:Copyright',
'exif:ExifVersion',
'exif:FlashpixVersion',
'exif:ColorSpace',
'exif:ComponentsConfiguration',
'exif:CompressedBitsPerPixel',
'exif:PixelXDimension',
'exif:PixelYDimension',
'exif:MakerNote',
'exif:UserComment',
'exif:RelatedSoundFile',
'exif:DateTimeOriginal',
'exif:DateTimeDigitized',
'exif:ExposureTime',
'exif:FNumber',
'exif:ExposureProgram',
'exif:SpectralSensitivity',
'exif:ISOSpeedRatings',
'exif:OECF',
'exif:ShutterSpeedValue',
'exif:ApertureValue',
'exif:BrightnessValue',
'exif:ExposureBiasValue',
'exif:MaxApertureValue',
'exif:SubjectDistance',
'exif:MeteringMode',
'exif:LightSource',
'exif:Flash',
'exif:FocalLength',
'exif:SubjectArea',
'exif:FlashEnergy',
'exif:SpatialFrequencyResponse',
'exif:FocalPlaneXResolution',
'exif:FocalPlaneYResolution',
'exif:FocalPlaneResolutionUnit',
'exif:SubjectLocation',
'exif:SensingMethod',
'exif:FileSource',
'exif:SceneType',
'exif:CFAPattern',
'exif:CustomRendered',
'exif:ExposureMode',
'exif:WhiteBalance',
'exif:DigitalZoomRatio',
'exif:FocalLengthIn35mmFilm',
'exif:SceneCaptureType',
'exif:GainControl',
'exif:Contrast',
'exif:Saturation',
'exif:Sharpness',
'exif:DeviceSettingDescription',
'exif:SubjectDistanceRange',
'exif:ImageUniqueID',
'exif:GPSVersionID',
'exif:GPSLatitude',
'exif:GPSLongitude',
'exif:GPSAltitudeRef',
'exif:GPSAltitude',
'exif:GPSTimeStamp',
'exif:GPSSatellites',
'exif:GPSStatus',
'exif:GPSMeasureMode',
'exif:GPSDOP',
'exif:GPSSpeedRef',
'exif:GPSSpeed',
'exif:GPSTrackRef',
'exif:GPSTrack',
'exif:GPSImgDirectionRef',
'exif:GPSImgDirection',
'exif:GPSMapDatum',
'exif:GPSDestLatitude',
'exif:GPSDestLongitude',
'exif:GPSDestBearingRef',
'exif:GPSDestBearing',
'exif:GPSDestDistanceRef',
'exif:GPSDestDistance',
'exif:GPSProcessingMethod',
'exif:GPSAreaInformation',
'exif:GPSDifferential',
'stDim:w',
'stDim:h',
'stDim:unit',
'xapGImg:height',
'xapGImg:width',
'xapGImg:format',
'xapGImg:image',
'stEvt:action',
'stEvt:instanceID',
'stEvt:parameters',
'stEvt:softwareAgent',
'stEvt:when',
'stRef:instanceID',
'stRef:documentID',
'stRef:versionID',
'stRef:renditionClass',
'stRef:renditionParams',
'stRef:manager',
'stRef:managerVariant',
'stRef:manageTo',
'stRef:manageUI',
'stVer:comments',
'stVer:event',
'stVer:modifyDate',
'stVer:modifier',
'stVer:version',
'stJob:name',
'stJob:id',
'stJob:url',
// Exif Flash
'exif:Fired',
'exif:Return',
'exif:Mode',
'exif:Function',
'exif:RedEyeMode',
// Exif OECF/SFR
'exif:Columns',
'exif:Rows',
'exif:Names',
'exif:Values',
// Exif CFAPattern
'exif:Columns',
'exif:Rows',
'exif:Values',
// Exif DeviceSettings
'exif:Columns',
'exif:Rows',
'exif:Settings',
);
/**
* Global Variable: JPEG_Segment_Names
*
* The names of the JPEG segment markers, indexed by their marker number
*/
$GLOBALS['JPEG_Segment_Names'] = array(
0x01 => 'TEM',
0x02 => 'RES',
0xC0 => 'SOF0',
0xC1 => 'SOF1',
0xC2 => 'SOF2',
0xC3 => 'SOF4',
0xC4 => 'DHT',
0xC5 => 'SOF5',
0xC6 => 'SOF6',
0xC7 => 'SOF7',
0xC8 => 'JPG',
0xC9 => 'SOF9',
0xCA => 'SOF10',
0xCB => 'SOF11',
0xCC => 'DAC',
0xCD => 'SOF13',
0xCE => 'SOF14',
0xCF => 'SOF15',
0xD0 => 'RST0',
0xD1 => 'RST1',
0xD2 => 'RST2',
0xD3 => 'RST3',
0xD4 => 'RST4',
0xD5 => 'RST5',
0xD6 => 'RST6',
0xD7 => 'RST7',
0xD8 => 'SOI',
0xD9 => 'EOI',
0xDA => 'SOS',
0xDB => 'DQT',
0xDC => 'DNL',
0xDD => 'DRI',
0xDE => 'DHP',
0xDF => 'EXP',
0xE0 => 'APP0',
0xE1 => 'APP1',
0xE2 => 'APP2',
0xE3 => 'APP3',
0xE4 => 'APP4',
0xE5 => 'APP5',
0xE6 => 'APP6',
0xE7 => 'APP7',
0xE8 => 'APP8',
0xE9 => 'APP9',
0xEA => 'APP10',
0xEB => 'APP11',
0xEC => 'APP12',
0xED => 'APP13',
0xEE => 'APP14',
0xEF => 'APP15',
0xF0 => 'JPG0',
0xF1 => 'JPG1',
0xF2 => 'JPG2',
0xF3 => 'JPG3',
0xF4 => 'JPG4',
0xF5 => 'JPG5',
0xF6 => 'JPG6',
0xF7 => 'JPG7',
0xF8 => 'JPG8',
0xF9 => 'JPG9',
0xFA => 'JPG10',
0xFB => 'JPG11',
0xFC => 'JPG12',
0xFD => 'JPG13',
0xFE => 'COM',
);
?>

561
modules/getid3/readme.txt Normal file
View file

@ -0,0 +1,561 @@
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
/////////////////////////////////////////////////////////////////
// //
// changelog.txt - part of getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
This code is released under the GNU GPL:
http://www.gnu.org/copyleft/gpl.html
+---------------------------------------------+
| If you do use this code somewhere, send me |
| an email and tell me how/where you used it. |
| |
| If you want to donate, there is a link on |
| http://www.getid3.org for PayPal donations. |
+---------------------------------------------+
Quick Start
===========================================================================
Q: How can I check that getID3() works on my server/files?
A: Unzip getID3() to a directory, then access /demos/demo.browse.php
Sourceforge Notification
===========================================================================
It's highly recommended that you sign up for notification from
Sourceforge for when new versions are released. Please visit:
http://sourceforge.net/project/showfiles.php?group_id=55859
and click the little "monitor package" icon/link. If you're
previously signed up for the mailing list, be aware that it has
been discontinued, only the automated Sourceforge notification
will be used from now on.
What does getID3() do?
===========================================================================
Reads & parses (to varying degrees):
¤ tags:
* APE (v1 and v2)
* ID3v1 (& ID3v1.1)
* ID3v2 (v2.4, v2.3, v2.2)
* Lyrics3 (v1 & v2)
¤ audio-lossy:
* MP3/MP2/MP1
* MPC / Musepack
* Ogg (Vorbis, OggFLAC, Speex)
* AC3
* DTS
* RealAudio
* Speex
* DSS
* VQF
¤ audio-lossless:
* AIFF
* AU
* Bonk
* CD-audio (*.cda)
* FLAC
* LA (Lossless Audio)
* LiteWave
* LPAC
* MIDI
* Monkey's Audio
* OptimFROG
* RKAU
* Shorten
* TTA
* VOC
* WAV (RIFF)
* WavPack
¤ audio-video:
* ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
* AVI (RIFF)
* Flash
* Matroska (MKV)
* MPEG-1 / MPEG-2
* NSV (Nullsoft Streaming Video)
* Quicktime
* RealVideo
¤ still image:
* BMP
* GIF
* JPEG
* PNG
* TIFF
* SWF (Flash)
* PhotoCD
¤ data:
* ISO-9660 CD-ROM image (directory structure)
* SZIP (limited support)
* ZIP (directory structure)
* TAR
* CUE
Writes:
* ID3v1 (& ID3v1.1)
* ID3v2 (v2.3 & v2.4)
* VorbisComment on OggVorbis
* VorbisComment on FLAC (not OggFLAC)
* APE v2
* Lyrics3 (delete only)
Requirements
===========================================================================
* PHP 4.2.0 (or higher) for getID3() 1.7.x (and earlier)
* PHP 5.3.0 (or higher) for getID3() 1.8.x (and up)
* PHP 5.0.0 (or higher) for getID3() 2.0.x (and up)
* at least 4MB memory for PHP. 8MB is highly recommended.
12MB is required with all modules loaded.
Usage
===========================================================================
See /demos/demo.basic.php for a very basic use of getID3() with no
fancy output, just scanning one file.
See structure.txt for the returned data structure.
*> For an example of a complete directory-browsing, <*
*> file-scanning implementation of getID3(), please run <*
*> /demos/demo.browse.php <*
See /demos/demo.mysql.php for a sample recursive scanning code that
scans every file in a given directory, and all sub-directories, stores
the results in a database and allows various analysis / maintenance
operations
To analyze remote files over HTTP or FTP you need to copy the file
locally first before running getID3(). Your code would look something
like this:
// Copy remote file locally to scan with getID3()
$remotefilename = 'http://www.example.com/filename.mp3';
if ($fp_remote = fopen($remotefilename, 'rb')) {
$localtempfilename = tempnam('/tmp', 'getID3');
if ($fp_local = fopen($localtempfilename, 'wb')) {
while ($buffer = fread($fp_remote, 8192)) {
fwrite($fp_local, $buffer);
}
fclose($fp_local);
// Initialize getID3 engine
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($filename);
// Delete temporary file
unlink($localtempfilename);
}
fclose($fp_remote);
}
See /demos/demo.write.php for how to write tags.
What does the returned data structure look like?
===========================================================================
See structure.txt
It is recommended that you look at the output of
/demos/demo.browse.php scanning the file(s) you're interested in to
confirm what data is actually returned for any particular filetype in
general, and your files in particular, as the actual data returned
may vary considerably depending on what information is available in
the file itself.
Notes
===========================================================================
getID3() 1.x:
If the format parser encounters a critical problem, it will return
something in $fileinfo['error'], describing the encountered error. If
a less critical error or notice is generated it will appear in
$fileinfo['warning']. Both keys may contain more than one warning or
error. If something is returned in ['error'] then the file was not
correctly parsed and returned data may or may not be correct and/or
complete. If something is returned in ['warning'] (and not ['error'])
then the data that is returned is OK - usually getID3() is reporting
errors in the file that have been worked around due to known bugs in
other programs. Some warnings may indicate that the data that is
returned is OK but that some data could not be extracted due to
errors in the file.
getID3() 2.x:
See above except errors are thrown (so you will only get one error).
Disclaimer
===========================================================================
getID3() has been tested on many systems, on many types of files,
under many operating systems, and is generally believe to be stable
and safe. That being said, there is still the chance there is an
undiscovered and/or unfixed bug that may potentially corrupt your
file, especially within the writing functions. By using getID3() you
agree that it's not my fault if any of your files are corrupted.
In fact, I'm not liable for anything :)
License
===========================================================================
GNU General Public License - see license.txt
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
FAQ:
Q: Can I use getID3() in my program? Do I need a commercial license?
A: You're generally free to use getID3 however you see fit. The only
case in which you would require a commercial license is if you're
selling your closed-source program that integrates getID3. If you
sell your program including a copy of getID3, that's fine as long
as you include a copy of the sourcecode when you sell it. Or you
can distribute your code without getID3 and say "download it from
getid3.sourceforge.net"
Future Plans
===========================================================================
* Writing support for Real
* Better support for MP4 container format
* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm)
* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669)
* Support for ACE (thanks Vince)
* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid)
* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header
* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding)
* Warn if MP3s change version mid-stream (in full-scan mode)
* check for corrupt/broken mid-file MP3 streams in histogram scan
* Support for lossless-compression formats
(http://www.firstpr.com.au/audiocomp/lossless/#Links)
(http://compression.ca/act-sound.html)
(http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
* Support for RIFF-INFO chunks
* http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
(thanks Nick Humfrey <njhØsurgeradio*co*uk>)
* http://abcavi.narod.ru/sof/abcavi/infotags.htm
(thanks Kibi)
* Better support for Bink video
* http://www.hr/josip/DSP/AudioFile2.html
* http://www.pcisys.net/~melanson/codecs/
* Detect mp3PRO
* Support for PSD
* Support for JPC
* Support for JP2
* Support for JPX
* Support for JB2
* Support for IFF
* Support for ICO
* Support for ANI
* Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl)
* Support for DVD-IFO (region, subtitles, aspect ratio, etc)
(thanks p*quaedackersØplanet*nl)
* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
(thanks n8n8Øyahoo*com)
* Support for a2b
* Optional scan-through-frames for AVI verification
(thanks rockcohenØmassive-interactive*nl)
* Support for TTF (thanks infoØbutterflyx*com)
* Support for DSS (http://www.getid3.org/phpBB2/viewtopic.php?t=171)
* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
http://www.getid3.org/phpBB2/viewtopic.php?t=182
* Support for AMR (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
* Support for 3gpp (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
* Parse XML data returned in Ogg comments
* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
* ID3v2 genre string creator function
* More complete parsing of JPG
* Support for all old-style ASF packets
* ASF/WMA/WMV tag writing
* Parse declared T??? ID3v2 text information frames, where appropriate
(thanks Christian Fritz for the idea)
* Recognize encoder:
http://www.guerillasoft.com/EncSpot2/index.html
http://ff123.net/identify.html
http://www.hydrogenaudio.org/?act=ST&f=16&t=9414
http://www.hydrogenaudio.org/?showtopic=11785
* Support for other OS/2 bitmap structures: Bitmap Array('BA'),
Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT')
http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
* Support for WavPack RAW mode
* ASF/WMA/WMV data packet parsing
* ID3v2FrameFlagsLookupTagAlter()
* ID3v2FrameFlagsLookupFileAlter()
* obey ID3v2 tag alter/preserve/discard rules
* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm
* proper checking for LINK/LNK frame validity in ID3v2 writing
* proper checking for ASPI-TLEN frame validity in ID3v2 writing
* proper checking for COMR frame validity in ID3v2 writing
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html
* decode GEOB ID3v2 structure as encoded by RealJukebox,
decode NCON ID3v2 structure as encoded by MusicMatch
(probably won't happen - the formats are proprietary)
Known Bugs/Issues in getID3() that may be fixed eventually
===========================================================================
* Cannot determine bitrate for MPEG video with VBR video data
(need documentation)
* Interlace/progressive cannot be determined for MPEG video
(need documentation)
* MIDI playtime is sometimes inaccurate
* AAC-RAW mode files cannot be identified
* WavPack-RAW mode files cannot be identified
* mp4 files report lots of "Unknown QuickTime atom type"
(need documentation)
* Encrypted ASF/WMA/WMV files warn about "unhandled GUID
ASF_Content_Encryption_Object"
* Bitrate split between audio and video cannot be calculated for
NSV, only the total bitrate. (need documentation)
* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the
problem of large VorbisComments spanning multiple Ogg pages, but
but only OggVorbis files can be processed with vorbiscomment.
* The version of "head" supplied with Mac OS 10.2.8 (maybe other
versions too) does only understands a single option (-n) and
therefore fails. getID3 ignores this and returns wrong md5_data.
Known Bugs/Issues in getID3() that cannot be fixed
--------------------------------------------------
* Files larger than 2GB cannot always be parsed fully by getID3()
due to limitations in the PHP filesystem functions.
NOTE: Since v1.7.8b3 there is partial support for larger-than-
2GB files, most of which will parse OK, as long as no critical
data is located beyond the 2GB offset.
Known will-work:
* ZIP (format doesn't support files >2GB)
* FLAC (current encoders don't support files >2GB)
Known will-not-work:
* ID3v1 tags (always located at end-of-file)
* Lyrics3 tags (always located at end-of-file)
* APE tags (always located at end-of-file)
Maybe-will-work:
* Quicktime (will work if needed metadata is before 2GB offset,
that is if the file has been hinted/optimized for streaming)
* RIFF.WAV (should work fine, but gives warnings about not being
able to parse all chunks)
* RIFF.AVI (playtime will probably be wrong, is only based on
"movi" chunk that fits in the first 2GB, should issue error
to show that playtime is incorrect. Other data should be mostly
correct, assuming that data is constant throughout the file)
Known Bugs/Issues in other programs
-----------------------------------
* Winamp (up to v2.80 at least) does not support ID3v2.4 tags,
only ID3v2.3
see: http://forums.winamp.com/showthread.php?postid=387524
* Some versions of Helium2 (www.helium2.com) do not write
ID3v2.4-compliant Frame Sizes, even though the tag is marked
as ID3v2.4) (detected by getID3())
* MP3ext V3.3.17 places a non-compliant padding string at the end
of the ID3v2 header. This is supposedly fixed in v3.4b21 but
only if you manually add a registry key. This fix is not yet
confirmed. (detected by getID3())
* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment
strings, supposed to be in the format "NAME=value" but actually
written just "value" (detected by getID3())
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
actually ABR or VBR.
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
other versions are too) writes ID3v2.3 comment tags using a
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
ID3v2.2-style frame name) (detected by getID3())
* MP2enc does not encode mono CBR MP2 files properly (half speed
sound and double playtime)
* MP2enc does not encode mono VBR MP2 files properly (actually
encoded as stereo)
* tooLAME does not encode mono VBR MP2 files properly (actually
encoded as stereo)
* AACenc encodes files in VBR mode (actually ABR) even if CBR is
specified
* AAC/ADIF - bitrate_mode = cbr for vbr files
* LAME 3.90-3.92 prepends one frame of null data (space for the
LAME/VBR header, but it never gets written) when encoding in CBR
mode with the DLL
* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed
to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for
TwinVQF v2.0 (detected by getID3())
* Ahead Nero encodes TwinVQF files 1 second shorter than they
should be
* AAC-ADTS files are always actually encoded VBR, even if CBR mode
is specified (the CBR-mode switches on the encoder enable ABR
mode, not CBR as such, but it's not possible to tell the
difference between such ABR files and true VBR)
* STREAMINFO.audio_signature in OggFLAC is always null. "The reason
it's like that is because there is no seeking support in
libOggFLAC yet, so it has no way to go back and write the
computed sum after encoding. Seeking support in Ogg FLAC is the
#1 item for the next release." - Josh Coalson (FLAC developer)
NOTE: getID3() will calculate md5_data in a method similar to
other file formats, but that value cannot be compared to the
md5_data value from FLAC data in a FLAC file format.
* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 &
v0.4.0 - getID3() will calculate md5_data in a method similar to
other file formats, but that value cannot be compared to the
md5_data value from FLAC v0.5.0+
* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with
a WCOM frame that has no data portion
* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis
files, thus making them corrupt.
* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the
last byte of data from an MP3 file when appending a new ID3v1 tag.
(detected by getID3())
* Lossless-Audio files encoded with and without the -noseek switch
do actually differ internally and therefore cannot match md5_data
* iTunes has been known to append a new ID3v1 tag on the end of an
existing ID3v1 tag when ID3v2 tag is also present
(detected by getID3())
Reference material:
===========================================================================
[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/]
* http://www.id3.org/id3v2.4.0-structure.txt
* http://www.id3.org/id3v2.4.0-frames.txt
* http://www.id3.org/id3v2.4.0-changes.txt
* http://www.id3.org/id3v2.3.0.txt
* http://www.id3.org/id3v2-00.txt
* http://www.id3.org/mp3frame.html
* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html <mathewhendry@hotmail.com>
* http://www.dv.co.yu/mpgscript/mpeghdr.htm
* http://www.mp3-tech.org/programmer/frame_header.html
* http://users.belgacom.net/gc247244/extra/tag.html
* http://gabriel.mp3-tech.org/mp3infotag.html
* http://www.id3.org/iso4217.html
* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
* http://www.xiph.org/ogg/vorbis/doc/framing.html
* http://www.xiph.org/ogg/vorbis/doc/v-comment.html
* http://leknor.com/code/php/class.ogg.php.txt
* http://www.id3.org/iso639-2.html
* http://www.id3.org/lyrics3.html
* http://www.id3.org/lyrics3200.html
* http://www.psc.edu/general/software/packages/ieee/ieee.html
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
* http://www.jmcgowan.com/avi.html
* http://www.wotsit.org/
* http://www.herdsoft.com/ti/davincie/davp3xo2.htm
* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html
* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org)
* http://midistudio.com/Help/GMSpecs_Patches.htm
* http://www.xiph.org/archives/vorbis/200109/0459.html
* http://www.replaygain.org/
* http://www.lossless-audio.com/
* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe
* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf
* http://www.uni-jena.de/~pfk/mpp/sv8/
* http://jfaul.de/atl/
* http://www.uni-jena.de/~pfk/mpp/
* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html
* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm
* http://www.fastgraph.com/help/bmp_os2_header_format.html
* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
* http://flac.sourceforge.net/format.html
* http://www.research.att.com/projects/mpegaudio/mpeg2.html
* http://www.audiocoding.com/wiki/index.php?page=AAC
* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm
* http://www.nullsoft.com/nsv/
* http://www.wotsit.org/download.asp?f=iso9660
* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
* http://www.cdroller.com/htm/readdata.html
* http://www.speex.org/manual/node10.html
* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc
* http://www.faqs.org/rfcs/rfc2361.html
* http://ghido.shelter.ro/
* http://www.ebu.ch/tech_t3285.pdf
* http://www.sr.se/utveckling/tu/bwf
* http://ftp.aessc.org/pub/aes46-2002.pdf
* http://cartchunk.org:8080/
* http://www.broadcastpapers.com/radio/cartchunk01.htm
* http://www.hr/josip/DSP/AudioFile2.html
* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html
* http://www.pure-mac.com/extkey.html
* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt
* http://www.headbands.com/gspot/
* http://www.openswf.org/spec/SWFfileformat.html
* http://j-faul.virtualave.net/
* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
* http://sswf.sourceforge.net/SWFalexref.html
* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
* http://developer.apple.com/quicktime/icefloe/dispatch012.html
* http://www.csdn.net/Dev/Format/graphics/PCD.htm
* http://tta.iszf.irk.ru/
* http://www.atsc.org/standards/a_52a.pdf
* http://www.alanwood.net/unicode/
* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
* http://www.its.msstate.edu/net/real/reports/config/tags.stats
* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
* http://brennan.young.net/Comp/LiveStage/things.html
* http://www.multiweb.cz/twoinches/MP3inside.htm
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
* http://www.unicode.org/unicode/faq/utf_bom.html
* http://tta.corecodec.org/?menu=format
* http://www.scvi.net/nsvformat.htm
* http://pda.etsi.org/pda/queryform.asp
* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
* http://trac.musepack.net/trac/wiki/SV8Specification
* http://wyday.com/cuesharp/specification.php

2251
modules/getid3/structure.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,264 +1,227 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // write.apetag.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for writing APE tags //
// +----------------------------------------------------------------------+ // dependencies: module.tag.apetag.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | write.apetag.php |
// | writing module for ape tags |
// | dependencies: module.tag.apetag.php |
// | dependencies: module.tag.id3v1.php |
// | dependencies: module.tag.lyrics3.php |
// +----------------------------------------------------------------------+
//
// $Id: write.apetag.php,v 1.9 2006/11/20 16:13:31 ah Exp $
class getid3_write_apetag extends getid3_handler_write getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
class getid3_write_apetag
{ {
public $comments; 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
public function read() { var $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here
$engine = new getid3;
$engine->filename = $this->filename; function getid3_write_apetag() {
$engine->fp = fopen($this->filename, 'rb'); return true;
$engine->include_module('tag.apetag'); }
$tag = new getid3_apetag($engine); function WriteAPEtag() {
$tag->Analyze(); // NOTE: All data passed to this function must be UTF-8 format
if (!isset($engine->info['ape']['comments'])) { $getID3 = new getID3;
return; $ThisFileInfo = $getID3->analyze($this->filename);
}
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
$this->comments = $engine->info['ape']['comments']; if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
// Current APE tag between Lyrics3 and ID3v1/EOF
// convert single element arrays to string // This break Lyrics3 functionality
foreach ($this->comments as $key => $value) { if (!$this->DeleteAPEtag()) {
if (sizeof($value) == 1) { return false;
$this->comments[$key] = $value[0]; }
} $ThisFileInfo = $getID3->analyze($this->filename);
} }
}
return true;
} 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) {
public function write() { 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];
// remove existing apetag }
$this->remove(); }
}
$engine = new getid3;
$engine->filename = $this->filename; if ($APEtag = $this->GenerateAPEtag()) {
$engine->fp = fopen($this->filename, 'rb'); if ($fp = @fopen($this->filename, 'a+b')) {
$engine->include_module('tag.id3v1'); $oldignoreuserabort = ignore_user_abort(true);
$engine->include_module('tag.lyrics3'); flock($fp, LOCK_EX);
$tag = new getid3_id3v1($engine); $PostAPEdataOffset = $ThisFileInfo['avdataend'];
$tag->Analyze(); if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
$tag = new getid3_lyrics3($engine); }
$tag->Analyze(); if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
$apetag = $this->generate_tag(); }
fseek($fp, $PostAPEdataOffset, SEEK_SET);
if (!$fp = @fopen($this->filename, 'a+b')) { $PostAPEdata = '';
throw new getid3_exception('Could not open a+b: ' . $this->filename); if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
} $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
}
// init: audio ends at eof
$post_audio_offset = filesize($this->filename); fseek($fp, $PostAPEdataOffset, SEEK_SET);
if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
// lyrics3 tag present fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
if (@$engine->info['lyrics3']['tag_offset_start']) { }
ftruncate($fp, ftell($fp));
// audio ends before lyrics3 tag fwrite($fp, $APEtag, strlen($APEtag));
$post_audio_offset = @$engine->info['lyrics3']['tag_offset_start']; if (!empty($PostAPEdata)) {
} fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
}
// id3v1 tag present flock($fp, LOCK_UN);
elseif (@$engine->info['id3v1']['tag_offset_start']) { fclose($fp);
ignore_user_abort($oldignoreuserabort);
// audio ends before id3v1 tag return true;
$post_audio_offset = $engine->info['id3v1']['tag_offset_start'];
} }
return false;
// seek to end of audio data }
fseek($fp, $post_audio_offset, SEEK_SET); return false;
}
// save data after audio data
$post_audio_data = ''; function DeleteAPEtag() {
if (filesize($this->filename) > $post_audio_offset) { $getID3 = new getID3;
$post_audio_data = fread($fp, filesize($this->filename) - $post_audio_offset); $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')) {
// truncate file before start of new apetag
fseek($fp, $post_audio_offset, SEEK_SET); flock($fp, LOCK_EX);
ftruncate($fp, ftell($fp)); $oldignoreuserabort = ignore_user_abort(true);
// write new apetag fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
fwrite($fp, $apetag, strlen($apetag)); $DataAfterAPE = '';
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
// rewrite data after audio $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
if (!empty($post_audio_data)) { }
fwrite($fp, $post_audio_data, strlen($post_audio_data));
} ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
fclose($fp);
clearstatcache(); if (!empty($DataAfterAPE)) {
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
return true; }
}
flock($fp, LOCK_UN);
fclose($fp);
public function remove() { ignore_user_abort($oldignoreuserabort);
$engine = new getid3; return true;
$engine->filename = $this->filename;
$engine->fp = fopen($this->filename, 'rb'); }
$engine->include_module('tag.apetag'); return false;
}
$tag = new getid3_apetag($engine); return true;
$tag->Analyze(); }
if (isset($engine->info['ape']['tag_offset_start']) && isset($engine->info['ape']['tag_offset_end'])) {
function GenerateAPEtag() {
if (!$fp = @fopen($this->filename, 'a+b')) { // NOTE: All data passed to this function must be UTF-8 format
throw new getid3_exception('Could not open a+b: ' . $this->filename);
} $items = array();
if (!is_array($this->tag_data)) {
// get data after apetag return false;
if (filesize($this->filename) > $engine->info['ape']['tag_offset_end']) { }
fseek($fp, $engine->info['ape']['tag_offset_end'], SEEK_SET); foreach ($this->tag_data as $key => $arrayofvalues) {
$data_after_ape = fread($fp, filesize($this->filename) - $engine->info['ape']['tag_offset_end']); if (!is_array($arrayofvalues)) {
} return false;
}
// truncate file before start of apetag
ftruncate($fp, $engine->info['ape']['tag_offset_start']); $valuestring = '';
foreach ($arrayofvalues as $value) {
// rewrite data after apetag $valuestring .= str_replace("\x00", '', $value)."\x00";
if (isset($data_after_ape)) { }
fseek($fp, $engine->info['ape']['tag_offset_start'], SEEK_SET); $valuestring = rtrim($valuestring, "\x00");
fwrite($fp, $data_after_ape, strlen($data_after_ape));
} // Length of the assigned value in bytes
$tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
fclose($fp);
clearstatcache(); //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
} $tagitem .= "\x00\x00\x00\x00";
// success when removing non-existant tag $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
return true; $tagitem .= $valuestring;
}
$items[] = $tagitem;
protected function generate_tag() { }
// NOTE: All data passed to this function must be UTF-8 format return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
}
$items = array();
if (!is_array($this->comments)) { function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
throw new getid3_exception('Cannot write empty tag, use remove() instead.'); $tagdatalength = 0;
} foreach ($items as $itemdata) {
$tagdatalength += strlen($itemdata);
foreach ($this->comments as $key => $values) { }
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html $APEheader = 'APETAGEX';
// A case-insensitive vobiscomment field name that may consist of ASCII 0x20 through 0x7E. $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through 0x7A inclusive (a-z). $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
if (preg_match("/[^\x20-\x7E]/", $key)) { $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
throw new getid3_exception('Field name "' . $key . '" contains invalid character(s).'); $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
} $APEheader .= str_repeat("\x00", 8);
$key = strtolower($key); return $APEheader;
}
// convert single value comment to array
if (!is_array($values)) { function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
$values = array ($values); $APEtagFlags = array_fill(0, 4, 0);
} if ($header) {
$APEtagFlags[0] |= 0x80; // Tag contains a header
$value_array = array (); }
foreach ($values as $value) { if (!$footer) {
$value_array[] = str_replace("\x00", '', $value); $APEtagFlags[0] |= 0x40; // Tag contains no footer
} }
$value_string = implode("\x00", $value_array); if ($isheader) {
$APEtagFlags[0] |= 0x20; // This is the header, not the footer
// length of the assigned value in bytes }
$tag_item = getid3_lib::LittleEndian2String(strlen($value_string), 4);
// 0: Item contains text information coded in UTF-8
$tag_item .= "\x00\x00\x00\x00" . $key . "\x00" . $value_string; // 1: Item contains binary information °)
// 2: Item is a locator of external stored information °°)
$items[] = $tag_item; // 3: reserved
} $APEtagFlags[3] |= ($encodingid << 1);
return $this->generate_header_footer($items, true) . implode('', $items) . $this->generate_header_footer($items, false); if ($readonly) {
} $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
}
protected function generate_header_footer(&$items, $is_header=false) { return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
}
$comments_length = 0;
foreach ($items as $item_data) { function CleanAPEtagItemKey($itemkey) {
$comments_length += strlen($item_data); $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
}
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
$header = 'APETAGEX'; switch (strtoupper($itemkey)) {
$header .= getid3_lib::LittleEndian2String(2000, 4); case 'EAN/UPC':
$header .= getid3_lib::LittleEndian2String(32 + $comments_length, 4); case 'ISBN':
$header .= getid3_lib::LittleEndian2String(count($items), 4); case 'LC':
$header .= $this->generate_flags(true, true, $is_header, 0, false); case 'ISRC':
$header .= str_repeat("\x00", 8); $itemkey = strtoupper($itemkey);
break;
return $header;
} default:
$itemkey = ucwords($itemkey);
break;
protected function generate_flags($header=true, $footer=true, $is_header=false, $encoding_id=0, $read_only=false) { }
return $itemkey;
$flags = array_fill(0, 4, 0);
}
// Tag contains a header
if ($header) {
$flags[0] |= 0x80;
}
// Tag contains no footer
if (!$footer) {
$flags[0] |= 0x40;
}
// This is the header, not the footer
if ($is_header) {
$flags[0] |= 0x20;
}
// 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
$flags[3] |= ($encoding_id << 1);
// Tag or Item is Read Only
if ($read_only) {
$flags[3] |= 0x01;
}
return chr($flags[3]).chr($flags[2]).chr($flags[1]).chr($flags[0]);
}
} }

View file

@ -1,156 +1,118 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // write.id3v1.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for writing ID3v1 tags //
// +----------------------------------------------------------------------+ // dependencies: module.tag.id3v1.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | write.id3v1.php |
// | writing module for id3v1 tags |
// | dependencies: module.tag.id3v1.php. |
// +----------------------------------------------------------------------+
//
// $Id: write.id3v1.php,v 1.15 2006/11/20 16:09:33 ah Exp $
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
class getid3_write_id3v1
class getid3_write_id3v1 extends getid3_handler_write
{ {
public $title; var $filename;
public $artist; var $tag_data;
public $album; var $warnings = array(); // any non-critical errors will be stored here
public $year; var $errors = array(); // any critical errors will be stored here
public $genre_id;
public $genre;
public $comment;
public $track;
function getid3_write_id3v1() {
return true;
}
public function read() { 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;
}
$engine = new getid3; // File MUST be writeable - CHMOD(646) at least
$engine->filename = $this->filename; if (is_writeable($this->filename)) {
$engine->fp = fopen($this->filename, 'rb'); if ($fp_source = @fopen($this->filename, 'r+b')) {
$engine->include_module('tag.id3v1');
$tag = new getid3_id3v1($engine); fseek($fp_source, -128, SEEK_END);
$tag->Analyze(); 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'] : '')));
if (!isset($engine->info['id3v1'])) { $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
return; @$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;
$this->title = $engine->info['id3v1']['title']; } else {
$this->artist = $engine->info['id3v1']['artist']; $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
$this->album = $engine->info['id3v1']['album']; return false;
$this->year = $engine->info['id3v1']['year']; }
$this->genre_id = $engine->info['id3v1']['genre_id']; }
$this->genre = $engine->info['id3v1']['genre']; $this->errors[] = 'File is not writeable: '.$this->filename;
$this->comment = $engine->info['id3v1']['comment']; return false;
$this->track = $engine->info['id3v1']['track']; }
return true; 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;
}
public function write() { function RemoveID3v1() {
if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
return false;
}
if (!$fp = @fopen($this->filename, 'r+b')) { // File MUST be writeable - CHMOD(646) at least
throw new getid3_exception('Could not open r+b: ' . $this->filename); if (is_writeable($this->filename)) {
} if ($fp_source = @fopen($this->filename, 'r+b')) {
// seek to end minus 128 bytes fseek($fp_source, -128, SEEK_END);
fseek($fp, -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;
// overwrite existing ID3v1 tag } else {
if (fread($fp, 3) == 'TAG') { $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
fseek($fp, -128, SEEK_END); }
} } else {
$this->errors[] = $this->filename.' is not writeable';
// append new ID3v1 tag }
else { return false;
fseek($fp, 0, SEEK_END); }
}
fwrite($fp, $this->generate_tag(), 128);
fclose($fp);
clearstatcache();
return true;
}
protected function generate_tag() {
$result = 'TAG';
$result .= str_pad(trim(substr($this->title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$result .= str_pad(trim(substr($this->artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$result .= str_pad(trim(substr($this->album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$result .= str_pad(trim(substr($this->year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
if (!empty($this->track) && ($this->track > 0) && ($this->track <= 255)) {
$result .= str_pad(trim(substr($this->comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
$result .= "\x00";
$result .= chr($this->track);
}
else {
$result .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
}
// both genre and genre_id set
if ($this->genre && $this->genre_id) {
if ($this->genre != getid3_id3v1::LookupGenreName($this->genre_id)) {
throw new getid3_exception('Genre and genre_id does not match. Unset one and the other will be determined automatically.');
}
}
// only genre set
elseif ($this->genre) {
$this->genre_id = getid3_id3v1::LookupGenreID($this->genre);
}
// only genre_id set
else {
if ($this->genre_id < 0 || $this->genre_id > 147) {
$this->genre_id = 255; // 'unknown' genre
}
$this->genre = getid3_id3v1::LookupGenreName($this->genre_id);
}
$result .= chr(intval($this->genre_id));
return $result;
}
public function remove() {
if (!$fp = @fopen($this->filename, 'r+b')) {
throw new getid3_exception('Could not open r+b: ' . $filename);
}
fseek($fp, -128, SEEK_END);
if (fread($fp, 3) == 'TAG') {
ftruncate($fp, filesize($this->filename) - 128);
fclose($fp);
clearstatcache();
}
// success when removing non-existant tag
return true;
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,210 +1,78 @@
<?php <?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ /// getID3() by James Heinrich <info@getid3.org> //
// | PHP version 5 | // available at http://getid3.sourceforge.net //
// +----------------------------------------------------------------------+ // or http://www.getid3.org //
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | /////////////////////////////////////////////////////////////////
// +----------------------------------------------------------------------+ // See readme.txt for more details //
// | This source file is subject to version 2 of the GPL license, | /////////////////////////////////////////////////////////////////
// | that is bundled with this package in the file license.txt and is | // //
// | available through the world-wide-web at the following url: | // write.lyrics3.php //
// | http://www.gnu.org/copyleft/gpl.html | // module for writing Lyrics3 tags //
// +----------------------------------------------------------------------+ // dependencies: module.tag.lyrics3.php //
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | // ///
// +----------------------------------------------------------------------+ /////////////////////////////////////////////////////////////////
// | Authors: James Heinrich <infoØgetid3*org> |
// | Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
// | write.lyrics3.php |
// | writing module for lyrics3 2.00 tags |
// | dependencies: module.tag.lyrics3.php. |
// | dependencies: module.tag.id3v1.php |
// +----------------------------------------------------------------------+
//
// $Id: write.lyrics3.php,v 1.5 2006/11/20 16:13:39 ah Exp $
class getid3_write_lyrics3
class getid3_write_lyrics3 extends getid3_handler_write
{ {
public $synched; var $filename;
public $random_inhibited; 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
public $lyrics; function getid3_write_lyrics3() {
public $comment; return true;
public $author; }
public $title;
public $artist; function WriteLyrics3() {
public $album; $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
public $images; 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;
}
public function read() {
$engine = new getid3;
$engine->filename = $this->filename;
$engine->fp = fopen($this->filename, 'rb');
$engine->include_module('tag.lyrics3');
$tag = new getid3_lyrics3($engine);
$tag->Analyze();
if (!isset($engine->info['lyrics3']['tag_offset_start'])) {
return;
}
$this->lyrics = @$engine->info['lyrics3']['raw']['LYR'];
$this->comment = @$engine->info['lyrics3']['raw']['INF'];
$this->author = @$engine->info['lyrics3']['raw']['AUT'];
$this->title = @$engine->info['lyrics3']['raw']['ETT'];
$this->artist = @$engine->info['lyrics3']['raw']['EAR'];
$this->album = @$engine->info['lyrics3']['raw']['EAL'];
$this->images = @$engine->info['lyrics3']['raw']['IMG'];
return true;
}
public function write() {
// remove existing apetag
$this->remove();
$engine = new getid3;
$engine->filename = $this->filename;
$engine->fp = fopen($this->filename, 'rb');
$engine->include_module('tag.id3v1');
$tag = new getid3_id3v1($engine);
$tag->Analyze();
$apetag = $this->generate_tag();
if (!$fp = @fopen($this->filename, 'a+b')) {
throw new getid3_exception('Could not open a+b: ' . $this->filename);
}
// init: audio ends at eof
$post_audio_offset = filesize($this->filename);
// id3v1 tag present
if (@$engine->info['id3v1']['tag_offset_start']) {
// audio ends before id3v1 tag
$post_audio_offset = $engine->info['id3v1']['tag_offset_start'];
}
// seek to end of audio data
fseek($fp, $post_audio_offset, SEEK_SET);
// save data after audio data
$post_audio_data = '';
if (filesize($this->filename) > $post_audio_offset) {
$post_audio_data = fread($fp, filesize($this->filename) - $post_audio_offset);
}
// truncate file before start of new apetag
fseek($fp, $post_audio_offset, SEEK_SET);
ftruncate($fp, ftell($fp));
// write new apetag
fwrite($fp, $apetag, strlen($apetag));
// rewrite data after audio
if (!empty($post_audio_data)) {
fwrite($fp, $post_audio_data, strlen($post_audio_data));
}
fclose($fp);
clearstatcache();
return true;
}
protected function generate_tag() {
// define fields
static $fields = array (
'lyrics' => 'LYR',
'comment' => 'INF',
'author' => 'AUT',
'title' => 'ETT',
'artist' => 'EAR',
'album' => 'EAL',
'images' => 'IMG'
);
// loop thru fields and add to frames
$frames = '';
foreach ($fields as $field => $frame_name) {
// field set?
if ($this->$field) {
$frames .= $frame_name . str_pad(strlen($this->$field), 5, '0', STR_PAD_LEFT) . $this->$field;
}
}
if (!$frames) {
throw new getid3_exception('Cannot write empty tag, use remove() instead.');
}
// header
$result = 'LYRICSBEGIN';
// indicator frame
$result .= 'IND00003' . ($this->lyrics ? '1' : '0') . ($this->synched ? '1' : '0') . ($this->random_inibited ? '1' : '0');
// other frames
$result .= $frames;
// footer
$result .= str_pad(strlen($result), 6, '0', STR_PAD_LEFT);
$result .= 'LYRICS200';
return $result;
}
public function remove() {
$engine = new getid3;
$engine->filename = $this->filename;
$engine->fp = fopen($this->filename, 'rb');
$engine->include_module('tag.lyrics3');
$tag = new getid3_lyrics3($engine);
$tag->Analyze();
if (isset($engine->info['lyrics3']['tag_offset_start']) && isset($engine->info['lyrics3']['tag_offset_end'])) {
if (!$fp = @fopen($this->filename, 'a+b')) {
throw new getid3_exception('Could not open a+b: ' . $this->filename);
}
// get data after tag
fseek($fp, $engine->info['lyrics3']['tag_offset_end'], SEEK_SET);
$data_after_lyrics3 = '';
if (filesize($this->filename) > $engine->info['lyrics3']['tag_offset_end']) {
$data_after_lyrics3 = fread($fp, filesize($this->filename) - $engine->info['lyrics3']['tag_offset_end']);
}
// truncate file before start of tag and seek to end
ftruncate($fp, $engine->info['lyrics3']['tag_offset_start']);
// rewrite data after tag
if (!empty($data_after_lyrics3)) {
fseek($fp, $engine->info['lyrics3']['tag_offset_start'], SEEK_SET);
fwrite($fp, $data_after_lyrics3, strlen($data_after_lyrics3));
}
fclose($fp);
clearstatcache();
}
// success when removing non-existant tag
return true;
}
} }
?> ?>

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 (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
// Create file with new comments
$tempcommentsfilename = tempnam((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ini_get('upload_tmp_dir')), '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 (preg_match('#(1|ON)#i', 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: *reg_replace() replaces nulls with empty string (not space)
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
}
}
?>

592
modules/getid3/write.php Normal file
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;
}
}
?>

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((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ini_get('upload_tmp_dir')), 'getID3')) {
ob_start();
if ($fp_temp = fopen($tempfilename, 'wb')) {
rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fwrite($fp_temp, $new_CONT_tag_data);
fseek($fp_source, $AfterOffset, SEEK_SET);
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
fwrite($fp_temp, $buffer, strlen($buffer));
}
fclose($fp_temp);
if (copy($tempfilename, $this->filename)) {
unlink($tempfilename);
fclose($fp_source);
return true;
}
unlink($tempfilename);
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
} else {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
ob_end_clean();
}
fclose($fp_source);
return false;
}
} else {
$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((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ini_get('upload_tmp_dir')), 'getID3')) {
ob_start();
if ($fp_temp = fopen($tempfilename, 'wb')) {
rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fseek($fp_source, $AfterOffset, SEEK_SET);
while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
fwrite($fp_temp, $buffer, strlen($buffer));
}
fclose($fp_temp);
if (copy($tempfilename, $this->filename)) {
unlink($tempfilename);
fclose($fp_source);
return true;
}
unlink($tempfilename);
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
} else {
$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
}
ob_end_clean();
}
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;
}
}
?>

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 (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
// Create file with new comments
$tempcommentsfilename = tempnam((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ini_get('upload_tmp_dir')), '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: *reg_replace() replaces nulls with empty string (not space)
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
}
}
?>

File diff suppressed because it is too large Load diff