mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-28 23:57:59 +01:00
Updated getID3
This commit is contained in:
parent
9616538d9c
commit
d43dd9ba40
91 changed files with 39356 additions and 38090 deletions
|
@ -18,6 +18,38 @@
|
||||||
Version History
|
Version History
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
1.9.14: [2017-03-27] James Heinrich
|
||||||
|
» Add experimental support for E-AC3
|
||||||
|
* bugfix (G:105): RIFF.WAVE.iXML multiple TIMESTAMP_SAMPLE_RATE
|
||||||
|
* bugfix (G:95): improperly initialized error/warning keys
|
||||||
|
* bugfix (G:94): ID3v2 write support for TXXX
|
||||||
|
* bugfix (G:93): all errors or warnings should pass through class method
|
||||||
|
|
||||||
|
1.9.13: [2016-12-14] James Heinrich
|
||||||
|
* bugfix (G:89): ID3v2.4 custom genres with slashes
|
||||||
|
* bugfix (G:88): large QuickTime files exceed PHP memory limit
|
||||||
|
* bugfix (G:87): ID3v2 write GRID data not working properly
|
||||||
|
* bugfix (G:86): Increase autoloading definitions
|
||||||
|
* bugfix (G:84): ID3v2 available writable frames list
|
||||||
|
* bugfix (G:82): ID3v2 datetime logic
|
||||||
|
* bugfix (G:80): attempt to autodetect ID3v1 encoding
|
||||||
|
* bugfix (G:77): add partial support of DSSv6
|
||||||
|
* bugfix (G:76): add mysqli version of caching extension
|
||||||
|
* bugfix (G:75): mysql cache max key length
|
||||||
|
* bugfix (G:71): custom error handler to catch exif_read_data() errors
|
||||||
|
* bugfix (G:71): add support for mb_convert_encoding
|
||||||
|
* bugfix (G:70): ID3v2 POPM / UFID
|
||||||
|
* bugfix (G:68): workaround broken iTunes ID3v2
|
||||||
|
* bugfix (G:48): Quicktime set MIME to video/mp4 where applicable
|
||||||
|
* bugfix (#1930) fread on pipes
|
||||||
|
* bugfix (#1926) relax ID3v2.IsValidURL check
|
||||||
|
|
||||||
|
1.9.12: [2016-03-02] James Heinrich
|
||||||
|
» Add support for Direct Stream Digital (DSD) /
|
||||||
|
DSD Storage Facility (DSF) file format
|
||||||
|
» Add detection (not parsing) of WebP image format
|
||||||
|
* bugfix (#1910): Quicktime embedded images
|
||||||
|
|
||||||
1.9.11: [2015-12-24] James Heinrich
|
1.9.11: [2015-12-24] James Heinrich
|
||||||
* bugfix (G:64): update constructor syntax for PHP 7
|
* bugfix (G:64): update constructor syntax for PHP 7
|
||||||
* bugfix (G:62): infinite loop in large PNG files
|
* bugfix (G:62): infinite loop in large PNG files
|
||||||
|
|
|
@ -10,6 +10,6 @@
|
||||||
"php": ">=5.3.0"
|
"php": ">=5.3.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": ["getid3/getid3.php"]
|
"classmap": ["getid3/"]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,275 +43,274 @@ require_once('../getid3/getid3.php');
|
||||||
* Class for extracting information from audio files with getID3().
|
* Class for extracting information from audio files with getID3().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AudioInfo
|
class AudioInfo {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private variables
|
* Private variables
|
||||||
*/
|
*/
|
||||||
var $result = null;
|
var $result = NULL;
|
||||||
var $info = null;
|
var $info = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function AudioInfo()
|
function AudioInfo() {
|
||||||
{
|
|
||||||
|
|
||||||
// Initialize getID3 engine
|
// Initialize getID3 engine
|
||||||
$this->getID3 = new getID3;
|
$this->getID3 = new getID3;
|
||||||
$this->getID3->option_md5_data = true;
|
$this->getID3->option_md5_data = true;
|
||||||
$this->getID3->option_md5_data_source = true;
|
$this->getID3->option_md5_data_source = true;
|
||||||
$this->getID3->encoding = 'UTF-8';
|
$this->getID3->encoding = 'UTF-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract information - only public function
|
* Extract information - only public function
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string file Audio file to extract info from.
|
* @param string file Audio file to extract info from.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Info($file)
|
function Info($file) {
|
||||||
{
|
|
||||||
|
|
||||||
// Analyze file
|
// Analyze file
|
||||||
$this->info = $this->getID3->analyze($file);
|
$this->info = $this->getID3->analyze($file);
|
||||||
|
|
||||||
// Exit here on error
|
// Exit here on error
|
||||||
if (isset($this->info['error'])) {
|
if (isset($this->info['error'])) {
|
||||||
return ['error' => $this->info['error']];
|
return array ('error' => $this->info['error']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init wrapper object
|
// Init wrapper object
|
||||||
$this->result = [];
|
$this->result = array();
|
||||||
$this->result['format_name'] = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'/'.(isset($this->info['audio']['dataformat']) ? $this->info['audio']['dataformat'] : '').(isset($this->info['video']['dataformat']) ? '/'.$this->info['video']['dataformat'] : '');
|
$this->result['format_name'] = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'/'.(isset($this->info['audio']['dataformat']) ? $this->info['audio']['dataformat'] : '').(isset($this->info['video']['dataformat']) ? '/'.$this->info['video']['dataformat'] : '');
|
||||||
$this->result['encoder_version'] = (isset($this->info['audio']['encoder']) ? $this->info['audio']['encoder'] : '');
|
$this->result['encoder_version'] = (isset($this->info['audio']['encoder']) ? $this->info['audio']['encoder'] : '');
|
||||||
$this->result['encoder_options'] = (isset($this->info['audio']['encoder_options']) ? $this->info['audio']['encoder_options'] : '');
|
$this->result['encoder_options'] = (isset($this->info['audio']['encoder_options']) ? $this->info['audio']['encoder_options'] : '');
|
||||||
$this->result['bitrate_mode'] = (isset($this->info['audio']['bitrate_mode']) ? $this->info['audio']['bitrate_mode'] : '');
|
$this->result['bitrate_mode'] = (isset($this->info['audio']['bitrate_mode']) ? $this->info['audio']['bitrate_mode'] : '');
|
||||||
$this->result['channels'] = (isset($this->info['audio']['channels']) ? $this->info['audio']['channels'] : '');
|
$this->result['channels'] = (isset($this->info['audio']['channels']) ? $this->info['audio']['channels'] : '');
|
||||||
$this->result['sample_rate'] = (isset($this->info['audio']['sample_rate']) ? $this->info['audio']['sample_rate'] : '');
|
$this->result['sample_rate'] = (isset($this->info['audio']['sample_rate']) ? $this->info['audio']['sample_rate'] : '');
|
||||||
$this->result['bits_per_sample'] = (isset($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : '');
|
$this->result['bits_per_sample'] = (isset($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : '');
|
||||||
$this->result['playing_time'] = (isset($this->info['playtime_seconds']) ? $this->info['playtime_seconds'] : '');
|
$this->result['playing_time'] = (isset($this->info['playtime_seconds']) ? $this->info['playtime_seconds'] : '');
|
||||||
$this->result['avg_bit_rate'] = (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : '');
|
$this->result['avg_bit_rate'] = (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : '');
|
||||||
$this->result['tags'] = (isset($this->info['tags']) ? $this->info['tags'] : '');
|
$this->result['tags'] = (isset($this->info['tags']) ? $this->info['tags'] : '');
|
||||||
$this->result['comments'] = (isset($this->info['comments']) ? $this->info['comments'] : '');
|
$this->result['comments'] = (isset($this->info['comments']) ? $this->info['comments'] : '');
|
||||||
$this->result['warning'] = (isset($this->info['warning']) ? $this->info['warning'] : '');
|
$this->result['warning'] = (isset($this->info['warning']) ? $this->info['warning'] : '');
|
||||||
$this->result['md5'] = (isset($this->info['md5_data']) ? $this->info['md5_data'] : '');
|
$this->result['md5'] = (isset($this->info['md5_data']) ? $this->info['md5_data'] : '');
|
||||||
|
|
||||||
// Post getID3() data handling based on file format
|
// Post getID3() data handling based on file format
|
||||||
$method = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'Info';
|
$method = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'Info';
|
||||||
if ($method && method_exists($this, $method)) {
|
if ($method && method_exists($this, $method)) {
|
||||||
$this->$method();
|
$this->$method();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->result;
|
return $this->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post-getID3() data handling for AAC files.
|
* post-getID3() data handling for AAC files.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function aacInfo()
|
function aacInfo() {
|
||||||
{
|
$this->result['format_name'] = 'AAC';
|
||||||
$this->result['format_name'] = 'AAC';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post-getID3() data handling for Wave files.
|
* post-getID3() data handling for Wave files.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function riffInfo()
|
function riffInfo() {
|
||||||
{
|
if ($this->info['audio']['dataformat'] == 'wav') {
|
||||||
if ($this->info['audio']['dataformat'] == 'wav') {
|
|
||||||
$this->result['format_name'] = 'Wave';
|
|
||||||
} elseif (preg_match('#^mp[1-3]$#', $this->info['audio']['dataformat'])) {
|
|
||||||
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
|
||||||
} else {
|
|
||||||
$this->result['format_name'] = 'riff/'.$this->info['audio']['dataformat'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$this->result['format_name'] = 'Wave';
|
||||||
|
|
||||||
|
} elseif (preg_match('#^mp[1-3]$#', $this->info['audio']['dataformat'])) {
|
||||||
|
|
||||||
|
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
||||||
|
|
||||||
/**
|
} else {
|
||||||
* * post-getID3() data handling for FLAC files.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function flacInfo()
|
$this->result['format_name'] = 'riff/'.$this->info['audio']['dataformat'];
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'FLAC';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post-getID3() data handling for Monkey's Audio files.
|
* * post-getID3() data handling for FLAC files.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function macInfo()
|
function flacInfo() {
|
||||||
{
|
$this->result['format_name'] = 'FLAC';
|
||||||
$this->result['format_name'] = 'Monkey\'s Audio';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post-getID3() data handling for Lossless Audio files.
|
* post-getID3() data handling for Monkey's Audio files.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function laInfo()
|
function macInfo() {
|
||||||
{
|
$this->result['format_name'] = 'Monkey\'s Audio';
|
||||||
$this->result['format_name'] = 'La';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post-getID3() data handling for Ogg Vorbis files.
|
* post-getID3() data handling for Lossless Audio files.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function oggInfo()
|
function laInfo() {
|
||||||
{
|
$this->result['format_name'] = 'La';
|
||||||
if ($this->info['audio']['dataformat'] == 'vorbis') {
|
}
|
||||||
$this->result['format_name'] = 'Ogg Vorbis';
|
|
||||||
} else if ($this->info['audio']['dataformat'] == 'flac') {
|
|
||||||
$this->result['format_name'] = 'Ogg FLAC';
|
|
||||||
} else if ($this->info['audio']['dataformat'] == 'speex') {
|
|
||||||
$this->result['format_name'] = 'Ogg Speex';
|
|
||||||
} else {
|
|
||||||
$this->result['format_name'] = 'Ogg '.$this->info['audio']['dataformat'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* post-getID3() data handling for Musepack files.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mpcInfo()
|
/**
|
||||||
{
|
* post-getID3() data handling for Ogg Vorbis files.
|
||||||
$this->result['format_name'] = 'Musepack';
|
*
|
||||||
}
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function oggInfo() {
|
||||||
|
if ($this->info['audio']['dataformat'] == 'vorbis') {
|
||||||
|
|
||||||
|
$this->result['format_name'] = 'Ogg Vorbis';
|
||||||
|
|
||||||
|
} else if ($this->info['audio']['dataformat'] == 'flac') {
|
||||||
|
|
||||||
/**
|
$this->result['format_name'] = 'Ogg FLAC';
|
||||||
* post-getID3() data handling for MPEG files.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mp3Info()
|
} else if ($this->info['audio']['dataformat'] == 'speex') {
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'MP3';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$this->result['format_name'] = 'Ogg Speex';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->result['format_name'] = 'Ogg '.$this->info['audio']['dataformat'];
|
||||||
|
|
||||||
/**
|
}
|
||||||
* post-getID3() data handling for MPEG files.
|
}
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mp2Info()
|
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'MP2';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for Musepack files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function mpcInfo() {
|
||||||
|
$this->result['format_name'] = 'Musepack';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* post-getID3() data handling for MPEG files.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mp1Info()
|
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'MP1';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for MPEG files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function mp3Info() {
|
||||||
|
$this->result['format_name'] = 'MP3';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* post-getID3() data handling for WMA files.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function asfInfo()
|
|
||||||
{
|
|
||||||
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for MPEG files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
function mp2Info() {
|
||||||
* post-getID3() data handling for Real files.
|
$this->result['format_name'] = 'MP2';
|
||||||
*
|
}
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function realInfo()
|
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'Real';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for MPEG files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
function mp1Info() {
|
||||||
* post-getID3() data handling for VQF files.
|
$this->result['format_name'] = 'MP1';
|
||||||
*
|
}
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for WMA files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function asfInfo() {
|
||||||
|
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for Real files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function realInfo() {
|
||||||
|
$this->result['format_name'] = 'Real';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post-getID3() data handling for VQF files.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function vqfInfo() {
|
||||||
|
$this->result['format_name'] = 'VQF';
|
||||||
|
}
|
||||||
|
|
||||||
function vqfInfo()
|
|
||||||
{
|
|
||||||
$this->result['format_name'] = 'VQF';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
|
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
|
||||||
|
|
||||||
|
|
||||||
// include getID3() library (can be in a different directory if full path is specified)
|
// include getID3() library (can be in a different directory if full path is specified)
|
||||||
require_once('../getid3/getid3.php');
|
require_once('../getid3/getid3.php');
|
||||||
|
|
||||||
|
@ -46,7 +45,10 @@ getid3_lib::CopyTagsToComments($ThisFileInfo);
|
||||||
//echo $ThisFileInfo['audio']['bitrate']; // audio bitrate
|
//echo $ThisFileInfo['audio']['bitrate']; // audio bitrate
|
||||||
//echo $ThisFileInfo['playtime_string']; // playtime in minutes:seconds, formatted string
|
//echo $ThisFileInfo['playtime_string']; // playtime in minutes:seconds, formatted string
|
||||||
|
|
||||||
/*
|
/* if you want to see all the tag data (from all tag formats), uncomment this line: */
|
||||||
if you want to see ALL the output, uncomment this line:
|
//echo '<pre>'.htmlentities(print_r($ThisFileInfo['comments'], true), ENT_SUBSTITUTE).'</pre>';
|
||||||
*/
|
|
||||||
//echo '<pre>'.htmlentities(print_r($ThisFileInfo, true)).'</pre>';
|
/* if you want to see ALL the output, uncomment this line: */
|
||||||
|
//echo '<pre>'.htmlentities(print_r($ThisFileInfo, true), ENT_SUBSTITUTE).'</pre>';
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -33,91 +33,103 @@
|
||||||
// CombineMultipleMP3sTo('sample.mp3', array(array('input.mp3', 0, 30))); // extract first 30 seconds of audio
|
// CombineMultipleMP3sTo('sample.mp3', array(array('input.mp3', 0, 30))); // extract first 30 seconds of audio
|
||||||
|
|
||||||
|
|
||||||
function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)
|
function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) {
|
||||||
{
|
|
||||||
|
|
||||||
foreach ($FilenamesIn as $nextinputfilename) {
|
foreach ($FilenamesIn as $nextinputfilename) {
|
||||||
if (is_array($nextinputfilename)) {
|
if (is_array($nextinputfilename)) {
|
||||||
$nextinputfilename = $nextinputfilename[0];
|
$nextinputfilename = $nextinputfilename[0];
|
||||||
}
|
}
|
||||||
if (!is_readable($nextinputfilename)) {
|
if (!is_readable($nextinputfilename)) {
|
||||||
echo 'Cannot read "'.$nextinputfilename.'"<BR>';
|
echo 'Cannot read "'.$nextinputfilename.'"<BR>';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((file_exists($FilenameOut) && !is_writeable($FilenameOut)) || (!file_exists($FilenameOut) && !is_writeable(dirname($FilenameOut)))) {
|
if ((file_exists($FilenameOut) && !is_writeable($FilenameOut)) || (!file_exists($FilenameOut) && !is_writeable(dirname($FilenameOut)))) {
|
||||||
echo 'Cannot write "'.$FilenameOut.'"<BR>';
|
echo 'Cannot write "'.$FilenameOut.'"<BR>';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once(dirname(__FILE__).'/../getid3/getid3.php');
|
require_once(dirname(__FILE__).'/../getid3/getid3.php');
|
||||||
ob_start();
|
ob_start();
|
||||||
if ($fp_output = fopen($FilenameOut, 'wb')) {
|
if ($fp_output = fopen($FilenameOut, 'wb')) {
|
||||||
ob_end_clean();
|
|
||||||
// Initialize getID3 engine
|
|
||||||
$getID3 = new getID3;
|
|
||||||
foreach ($FilenamesIn as $nextinputfilename) {
|
|
||||||
$startoffset = 0;
|
|
||||||
$length_seconds = 0;
|
|
||||||
if (is_array($nextinputfilename)) {
|
|
||||||
@list($nextinputfilename, $startoffset, $length_seconds) = $nextinputfilename;
|
|
||||||
}
|
|
||||||
$CurrentFileInfo = $getID3->analyze($nextinputfilename);
|
|
||||||
if ($CurrentFileInfo['fileformat'] == 'mp3') {
|
|
||||||
ob_start();
|
|
||||||
if ($fp_source = fopen($nextinputfilename, 'rb')) {
|
|
||||||
ob_end_clean();
|
|
||||||
$CurrentOutputPosition = ftell($fp_output);
|
|
||||||
|
|
||||||
// copy audio data from first file
|
ob_end_clean();
|
||||||
$start_offset_bytes = $CurrentFileInfo['avdataoffset'];
|
// Initialize getID3 engine
|
||||||
if ($startoffset > 0) { // start X seconds from start of audio
|
$getID3 = new getID3;
|
||||||
$start_offset_bytes = $CurrentFileInfo['avdataoffset'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
foreach ($FilenamesIn as $nextinputfilename) {
|
||||||
} elseif ($startoffset < 0) { // start X seconds from end of audio
|
$startoffset = 0;
|
||||||
$start_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
$length_seconds = 0;
|
||||||
}
|
if (is_array($nextinputfilename)) {
|
||||||
$start_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $start_offset_bytes));
|
@list($nextinputfilename, $startoffset, $length_seconds) = $nextinputfilename;
|
||||||
|
}
|
||||||
|
$CurrentFileInfo = $getID3->analyze($nextinputfilename);
|
||||||
|
if ($CurrentFileInfo['fileformat'] == 'mp3') {
|
||||||
|
|
||||||
$end_offset_bytes = $CurrentFileInfo['avdataend'];
|
ob_start();
|
||||||
if ($length_seconds > 0) { // seconds from start of audio
|
if ($fp_source = fopen($nextinputfilename, 'rb')) {
|
||||||
$end_offset_bytes = $start_offset_bytes + round($CurrentFileInfo['bitrate'] / 8 * $length_seconds);
|
|
||||||
} elseif ($length_seconds < 0) { // seconds from start of audio
|
|
||||||
$end_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
|
||||||
}
|
|
||||||
$end_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $end_offset_bytes));
|
|
||||||
|
|
||||||
if ($end_offset_bytes <= $start_offset_bytes) {
|
ob_end_clean();
|
||||||
echo 'failed to copy '.$nextinputfilename.' from '.$startoffset.'-seconds start for '.$length_seconds.'-seconds length (not enough data)';
|
$CurrentOutputPosition = ftell($fp_output);
|
||||||
fclose($fp_source);
|
|
||||||
fclose($fp_output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek($fp_source, $start_offset_bytes, SEEK_SET);
|
// copy audio data from first file
|
||||||
while (!feof($fp_source) && (ftell($fp_source) < $end_offset_bytes)) {
|
$start_offset_bytes = $CurrentFileInfo['avdataoffset'];
|
||||||
fwrite($fp_output, fread($fp_source, min(32768, $end_offset_bytes - ftell($fp_source))));
|
if ($startoffset > 0) { // start X seconds from start of audio
|
||||||
}
|
$start_offset_bytes = $CurrentFileInfo['avdataoffset'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
||||||
fclose($fp_source);
|
} elseif ($startoffset < 0) { // start X seconds from end of audio
|
||||||
} else {
|
$start_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
||||||
$errormessage = ob_get_contents();
|
}
|
||||||
ob_end_clean();
|
$start_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $start_offset_bytes));
|
||||||
echo 'failed to open '.$nextinputfilename.' for reading';
|
|
||||||
fclose($fp_output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo $nextinputfilename.' is not MP3 format';
|
|
||||||
fclose($fp_output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$errormessage = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
echo 'failed to open '.$FilenameOut.' for writing';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($fp_output);
|
$end_offset_bytes = $CurrentFileInfo['avdataend'];
|
||||||
return true;
|
if ($length_seconds > 0) { // seconds from start of audio
|
||||||
|
$end_offset_bytes = $start_offset_bytes + round($CurrentFileInfo['bitrate'] / 8 * $length_seconds);
|
||||||
|
} elseif ($length_seconds < 0) { // seconds from start of audio
|
||||||
|
$end_offset_bytes = $CurrentFileInfo['avdataend'] + round($CurrentFileInfo['bitrate'] / 8 * $startoffset);
|
||||||
|
}
|
||||||
|
$end_offset_bytes = max($CurrentFileInfo['avdataoffset'], min($CurrentFileInfo['avdataend'], $end_offset_bytes));
|
||||||
|
|
||||||
|
if ($end_offset_bytes <= $start_offset_bytes) {
|
||||||
|
echo 'failed to copy '.$nextinputfilename.' from '.$startoffset.'-seconds start for '.$length_seconds.'-seconds length (not enough data)';
|
||||||
|
fclose($fp_source);
|
||||||
|
fclose($fp_output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek($fp_source, $start_offset_bytes, SEEK_SET);
|
||||||
|
while (!feof($fp_source) && (ftell($fp_source) < $end_offset_bytes)) {
|
||||||
|
fwrite($fp_output, fread($fp_source, min(32768, $end_offset_bytes - ftell($fp_source))));
|
||||||
|
}
|
||||||
|
fclose($fp_source);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$errormessage = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
echo 'failed to open '.$nextinputfilename.' for reading';
|
||||||
|
fclose($fp_output);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
echo $nextinputfilename.' is not MP3 format';
|
||||||
|
fclose($fp_output);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$errormessage = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
echo 'failed to open '.$FilenameOut.' for writing';
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($fp_output);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,50 +20,55 @@ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www
|
||||||
echo '<html><head><title>getID3 demos - MIME type only</title><style type="text/css">BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</style></head><body>';
|
echo '<html><head><title>getID3 demos - MIME type only</title><style type="text/css">BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</style></head><body>';
|
||||||
|
|
||||||
if (!empty($_REQUEST['filename'])) {
|
if (!empty($_REQUEST['filename'])) {
|
||||||
echo 'The file "'.htmlentities($_REQUEST['filename']).'" has a MIME type of "'.htmlentities(GetMIMEtype($_REQUEST['filename'])).'"';
|
|
||||||
|
echo 'The file "'.htmlentities($_REQUEST['filename']).'" has a MIME type of "'.htmlentities(GetMIMEtype($_REQUEST['filename'])).'"';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
echo 'Usage: <span style="font-family: monospace;">'.htmlentities($_SERVER['PHP_SELF']).'?filename=<i>filename.ext</i></span>';
|
|
||||||
|
echo 'Usage: <span style="font-family: monospace;">'.htmlentities($_SERVER['PHP_SELF']).'?filename=<i>filename.ext</i></span>';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetMIMEtype($filename)
|
function GetMIMEtype($filename) {
|
||||||
{
|
$filename = realpath($filename);
|
||||||
$filename = realpath($filename);
|
if (!file_exists($filename)) {
|
||||||
if (!file_exists($filename)) {
|
echo 'File does not exist: "'.htmlentities($filename).'"<br>';
|
||||||
echo 'File does not exist: "'.htmlentities($filename).'"<br>';
|
return '';
|
||||||
return '';
|
} elseif (!is_readable($filename)) {
|
||||||
} elseif (!is_readable($filename)) {
|
echo 'File is not readable: "'.htmlentities($filename).'"<br>';
|
||||||
echo 'File is not readable: "'.htmlentities($filename).'"<br>';
|
return '';
|
||||||
return '';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// include getID3() library (can be in a different directory if full path is specified)
|
// include getID3() library (can be in a different directory if full path is specified)
|
||||||
require_once('../getid3/getid3.php');
|
require_once('../getid3/getid3.php');
|
||||||
// Initialize getID3 engine
|
// Initialize getID3 engine
|
||||||
$getID3 = new getID3;
|
$getID3 = new getID3;
|
||||||
|
|
||||||
$DeterminedMIMEtype = '';
|
$DeterminedMIMEtype = '';
|
||||||
if ($fp = fopen($filename, 'rb')) {
|
if ($fp = fopen($filename, 'rb')) {
|
||||||
$getID3->openfile($filename);
|
$getID3->openfile($filename);
|
||||||
if (empty($getID3->info['error'])) {
|
if (empty($getID3->info['error'])) {
|
||||||
// ID3v2 is the only tag format that might be prepended in front of files, and it's non-trivial to skip, easier just to parse it and know where to skip to
|
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
|
|
||||||
$getid3_id3v2 = new getid3_id3v2($getID3);
|
|
||||||
$getid3_id3v2->Analyze();
|
|
||||||
|
|
||||||
fseek($fp, $getID3->info['avdataoffset'], SEEK_SET);
|
// ID3v2 is the only tag format that might be prepended in front of files, and it's non-trivial to skip, easier just to parse it and know where to skip to
|
||||||
$formattest = fread($fp, 16); // 16 bytes is sufficient for any format except ISO CD-image
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
|
||||||
fclose($fp);
|
$getid3_id3v2 = new getid3_id3v2($getID3);
|
||||||
|
$getid3_id3v2->Analyze();
|
||||||
|
|
||||||
$DeterminedFormatInfo = $getID3->GetFileFormat($formattest);
|
fseek($fp, $getID3->info['avdataoffset'], SEEK_SET);
|
||||||
$DeterminedMIMEtype = $DeterminedFormatInfo['mime_type'];
|
$formattest = fread($fp, 16); // 16 bytes is sufficient for any format except ISO CD-image
|
||||||
} else {
|
fclose($fp);
|
||||||
echo 'Failed to getID3->openfile "'.htmlentities($filename).'"<br>';
|
|
||||||
}
|
$DeterminedFormatInfo = $getID3->GetFileFormat($formattest);
|
||||||
} else {
|
$DeterminedMIMEtype = $DeterminedFormatInfo['mime_type'];
|
||||||
echo 'Failed to fopen "'.htmlentities($filename).'"<br>';
|
|
||||||
}
|
} else {
|
||||||
return $DeterminedMIMEtype;
|
echo 'Failed to getID3->openfile "'.htmlentities($filename).'"<br>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo 'Failed to fopen "'.htmlentities($filename).'"<br>';
|
||||||
|
}
|
||||||
|
return $DeterminedMIMEtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '</body></html>';
|
echo '</body></html>';
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -32,23 +32,23 @@ $dir = opendir($DirectoryToScan);
|
||||||
echo '<table border="1" cellspacing="0" cellpadding="3">';
|
echo '<table border="1" cellspacing="0" cellpadding="3">';
|
||||||
echo '<tr><th>Filename</th><th>Artist</th><th>Title</th><th>Bitrate</th><th>Playtime</th></tr>';
|
echo '<tr><th>Filename</th><th>Artist</th><th>Title</th><th>Bitrate</th><th>Playtime</th></tr>';
|
||||||
while (($file = readdir($dir)) !== false) {
|
while (($file = readdir($dir)) !== false) {
|
||||||
$FullFileName = realpath($DirectoryToScan.'/'.$file);
|
$FullFileName = realpath($DirectoryToScan.'/'.$file);
|
||||||
if ((substr($file, 0, 1) != '.') && is_file($FullFileName)) {
|
if ((substr($file, 0, 1) != '.') && is_file($FullFileName)) {
|
||||||
set_time_limit(30);
|
set_time_limit(30);
|
||||||
|
|
||||||
$ThisFileInfo = $getID3->analyze($FullFileName);
|
$ThisFileInfo = $getID3->analyze($FullFileName);
|
||||||
|
|
||||||
getid3_lib::CopyTagsToComments($ThisFileInfo);
|
getid3_lib::CopyTagsToComments($ThisFileInfo);
|
||||||
|
|
||||||
// output desired information in whatever format you want
|
// output desired information in whatever format you want
|
||||||
echo '<tr>';
|
echo '<tr>';
|
||||||
echo '<td>'.htmlentities($ThisFileInfo['filenamepath']).'</td>';
|
echo '<td>'.htmlentities($ThisFileInfo['filenamepath']).'</td>';
|
||||||
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['artist']) ? implode('<br>', $ThisFileInfo['comments_html']['artist']) : chr(160)).'</td>';
|
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['artist']) ? implode('<br>', $ThisFileInfo['comments_html']['artist']) : chr(160)).'</td>';
|
||||||
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['title']) ? implode('<br>', $ThisFileInfo['comments_html']['title']) : chr(160)).'</td>';
|
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['title']) ? implode('<br>', $ThisFileInfo['comments_html']['title']) : chr(160)).'</td>';
|
||||||
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['audio']['bitrate']) ? round($ThisFileInfo['audio']['bitrate'] / 1000).' kbps' : chr(160)).'</td>';
|
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['audio']['bitrate']) ? round($ThisFileInfo['audio']['bitrate'] / 1000).' kbps' : chr(160)).'</td>';
|
||||||
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['playtime_string']) ? $ThisFileInfo['playtime_string'] : chr(160)).'</td>';
|
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['playtime_string']) ? $ThisFileInfo['playtime_string'] : chr(160)).'</td>';
|
||||||
echo '</tr>';
|
echo '</tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo '</table>';
|
echo '</table>';
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,14 @@
|
||||||
// ///
|
// ///
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
|
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
|
||||||
|
|
||||||
$TextEncoding = 'UTF-8';
|
$TextEncoding = 'UTF-8';
|
||||||
|
|
||||||
require_once('../getid3/getid3.php');
|
require_once('../getid3/getid3.php');
|
||||||
// Initialize getID3 engine
|
// Initialize getID3 engine
|
||||||
$getID3 = new getID3;
|
$getID3 = new getID3;
|
||||||
$getID3->setOption(['encoding'=>$TextEncoding]);
|
$getID3->setOption(array('encoding'=>$TextEncoding));
|
||||||
|
|
||||||
require_once('../getid3/write.php');
|
require_once('../getid3/write.php');
|
||||||
// Initialize getID3 tag-writing module
|
// Initialize getID3 tag-writing module
|
||||||
|
@ -28,7 +28,7 @@ $tagwriter = new getid3_writetags;
|
||||||
$tagwriter->filename = 'c:/file.mp3';
|
$tagwriter->filename = 'c:/file.mp3';
|
||||||
|
|
||||||
//$tagwriter->tagformats = array('id3v1', 'id3v2.3');
|
//$tagwriter->tagformats = array('id3v1', 'id3v2.3');
|
||||||
$tagwriter->tagformats = ['id3v2.3'];
|
$tagwriter->tagformats = array('id3v2.3');
|
||||||
|
|
||||||
// set various options (optional)
|
// set various options (optional)
|
||||||
$tagwriter->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 (experimental)
|
$tagwriter->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 (experimental)
|
||||||
|
@ -37,24 +37,25 @@ $tagwriter->tag_encoding = $TextEncoding;
|
||||||
$tagwriter->remove_other_tags = true;
|
$tagwriter->remove_other_tags = true;
|
||||||
|
|
||||||
// populate data array
|
// populate data array
|
||||||
$TagData = [
|
$TagData = array(
|
||||||
'title' => ['My Song'],
|
'title' => array('My Song'),
|
||||||
'artist' => ['The Artist'],
|
'artist' => array('The Artist'),
|
||||||
'album' => ['Greatest Hits'],
|
'album' => array('Greatest Hits'),
|
||||||
'year' => ['2004'],
|
'year' => array('2004'),
|
||||||
'genre' => ['Rock'],
|
'genre' => array('Rock'),
|
||||||
'comment' => ['excellent!'],
|
'comment' => array('excellent!'),
|
||||||
'track' => ['04/16'],
|
'track' => array('04/16'),
|
||||||
'popularimeter' => ['email'=>'user@example.net', 'rating'=>128, 'data'=>0],
|
'popularimeter' => array('email'=>'user@example.net', 'rating'=>128, 'data'=>0),
|
||||||
];
|
'unique_file_identifier' => array('ownerid'=>'user@example.net', 'data'=>md5(time())),
|
||||||
|
);
|
||||||
$tagwriter->tag_data = $TagData;
|
$tagwriter->tag_data = $TagData;
|
||||||
|
|
||||||
// write tags
|
// write tags
|
||||||
if ($tagwriter->WriteTags()) {
|
if ($tagwriter->WriteTags()) {
|
||||||
echo 'Successfully wrote tags<br>';
|
echo 'Successfully wrote tags<br>';
|
||||||
if (!empty($tagwriter->warnings)) {
|
if (!empty($tagwriter->warnings)) {
|
||||||
echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
|
echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
|
echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ echo '<html><head><title>getID3() - Sample tag writer</title></head><style type=
|
||||||
require_once('../getid3/getid3.php');
|
require_once('../getid3/getid3.php');
|
||||||
// Initialize getID3 engine
|
// Initialize getID3 engine
|
||||||
$getID3 = new getID3;
|
$getID3 = new getID3;
|
||||||
$getID3->setOption(['encoding'=>$TaggingFormat]);
|
$getID3->setOption(array('encoding'=>$TaggingFormat));
|
||||||
|
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
|
||||||
|
|
||||||
|
@ -36,224 +36,236 @@ $Filename = (isset($_REQUEST['Filename']) ? $_REQUEST['Filename'] : '');
|
||||||
|
|
||||||
|
|
||||||
if (isset($_POST['WriteTags'])) {
|
if (isset($_POST['WriteTags'])) {
|
||||||
$TagFormatsToWrite = (isset($_POST['TagFormatsToWrite']) ? $_POST['TagFormatsToWrite'] : []);
|
|
||||||
if (!empty($TagFormatsToWrite)) {
|
|
||||||
echo 'starting to write tag(s)<BR>';
|
|
||||||
|
|
||||||
$tagwriter = new getid3_writetags;
|
$TagFormatsToWrite = (isset($_POST['TagFormatsToWrite']) ? $_POST['TagFormatsToWrite'] : array());
|
||||||
$tagwriter->filename = $Filename;
|
if (!empty($TagFormatsToWrite)) {
|
||||||
$tagwriter->tagformats = $TagFormatsToWrite;
|
echo 'starting to write tag(s)<BR>';
|
||||||
$tagwriter->overwrite_tags = false;
|
|
||||||
$tagwriter->tag_encoding = $TaggingFormat;
|
|
||||||
if (!empty($_POST['remove_other_tags'])) {
|
|
||||||
$tagwriter->remove_other_tags = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$commonkeysarray = ['Title', 'Artist', 'Album', 'Year', 'Comment'];
|
$tagwriter = new getid3_writetags;
|
||||||
foreach ($commonkeysarray as $key) {
|
$tagwriter->filename = $Filename;
|
||||||
if (!empty($_POST[$key])) {
|
$tagwriter->tagformats = $TagFormatsToWrite;
|
||||||
$TagData[strtolower($key)][] = $_POST[$key];
|
$tagwriter->overwrite_tags = false;
|
||||||
}
|
$tagwriter->tag_encoding = $TaggingFormat;
|
||||||
}
|
if (!empty($_POST['remove_other_tags'])) {
|
||||||
if (!empty($_POST['Genre'])) {
|
$tagwriter->remove_other_tags = true;
|
||||||
$TagData['genre'][] = $_POST['Genre'];
|
}
|
||||||
}
|
|
||||||
if (!empty($_POST['GenreOther'])) {
|
|
||||||
$TagData['genre'][] = $_POST['GenreOther'];
|
|
||||||
}
|
|
||||||
if (!empty($_POST['Track'])) {
|
|
||||||
$TagData['track'][] = $_POST['Track'].(!empty($_POST['TracksTotal']) ? '/'.$_POST['TracksTotal'] : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_FILES['userfile']['tmp_name'])) {
|
$commonkeysarray = array('Title', 'Artist', 'Album', 'Year', 'Comment');
|
||||||
if (in_array('id3v2.4', $tagwriter->tagformats) || in_array('id3v2.3', $tagwriter->tagformats) || in_array('id3v2.2', $tagwriter->tagformats)) {
|
foreach ($commonkeysarray as $key) {
|
||||||
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
|
if (!empty($_POST[$key])) {
|
||||||
ob_start();
|
$TagData[strtolower($key)][] = $_POST[$key];
|
||||||
if ($fd = fopen($_FILES['userfile']['tmp_name'], 'rb')) {
|
}
|
||||||
ob_end_clean();
|
}
|
||||||
$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
|
if (!empty($_POST['Genre'])) {
|
||||||
fclose($fd);
|
$TagData['genre'][] = $_POST['Genre'];
|
||||||
|
}
|
||||||
|
if (!empty($_POST['GenreOther'])) {
|
||||||
|
$TagData['genre'][] = $_POST['GenreOther'];
|
||||||
|
}
|
||||||
|
if (!empty($_POST['Track'])) {
|
||||||
|
$TagData['track'][] = $_POST['Track'].(!empty($_POST['TracksTotal']) ? '/'.$_POST['TracksTotal'] : '');
|
||||||
|
}
|
||||||
|
|
||||||
list($APIC_width, $APIC_height, $APIC_imageTypeID) = GetImageSize($_FILES['userfile']['tmp_name']);
|
if (!empty($_FILES['userfile']['tmp_name'])) {
|
||||||
$imagetypes = [1=>'gif', 2=>'jpeg', 3=>'png'];
|
if (in_array('id3v2.4', $tagwriter->tagformats) || in_array('id3v2.3', $tagwriter->tagformats) || in_array('id3v2.2', $tagwriter->tagformats)) {
|
||||||
if (isset($imagetypes[$APIC_imageTypeID])) {
|
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
|
||||||
$TagData['attached_picture'][0]['data'] = $APICdata;
|
ob_start();
|
||||||
$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
|
if ($fd = fopen($_FILES['userfile']['tmp_name'], 'rb')) {
|
||||||
$TagData['attached_picture'][0]['description'] = $_FILES['userfile']['name'];
|
ob_end_clean();
|
||||||
$TagData['attached_picture'][0]['mime'] = 'image/'.$imagetypes[$APIC_imageTypeID];
|
$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
|
||||||
} else {
|
fclose ($fd);
|
||||||
echo '<b>invalid image format (only GIF, JPEG, PNG)</b><br>';
|
|
||||||
}
|
list($APIC_width, $APIC_height, $APIC_imageTypeID) = GetImageSize($_FILES['userfile']['tmp_name']);
|
||||||
} else {
|
$imagetypes = array(1=>'gif', 2=>'jpeg', 3=>'png');
|
||||||
$errormessage = ob_get_contents();
|
if (isset($imagetypes[$APIC_imageTypeID])) {
|
||||||
ob_end_clean();
|
|
||||||
echo '<b>cannot open '.$_FILES['userfile']['tmp_name'].'</b><br>';
|
$TagData['attached_picture'][0]['data'] = $APICdata;
|
||||||
}
|
$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
|
||||||
} else {
|
$TagData['attached_picture'][0]['description'] = $_FILES['userfile']['name'];
|
||||||
echo '<b>!is_uploaded_file('.$_FILES['userfile']['tmp_name'].')</b><br>';
|
$TagData['attached_picture'][0]['mime'] = 'image/'.$imagetypes[$APIC_imageTypeID];
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
echo '<b>WARNING:</b> Can only embed images for ID3v2<br>';
|
echo '<b>invalid image format (only GIF, JPEG, PNG)</b><br>';
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
$errormessage = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
echo '<b>cannot open '.$_FILES['userfile']['tmp_name'].'</b><br>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<b>!is_uploaded_file('.$_FILES['userfile']['tmp_name'].')</b><br>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<b>WARNING:</b> Can only embed images for ID3v2<br>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagwriter->tag_data = $TagData;
|
||||||
|
if ($tagwriter->WriteTags()) {
|
||||||
|
echo 'Successfully wrote tags<BR>';
|
||||||
|
if (!empty($tagwriter->warnings)) {
|
||||||
|
echo 'There were some warnings:<BLOCKQUOTE STYLE="background-color:#FFCC33; padding: 10px;">'.implode('<br><br>', $tagwriter->warnings).'</BLOCKQUOTE>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo 'Failed to write tags!<BLOCKQUOTE STYLE="background-color:#FF9999; padding: 10px;">'.implode('<br><br>', $tagwriter->errors).'</BLOCKQUOTE>';
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
echo 'WARNING: no tag formats selected for writing - nothing written';
|
||||||
|
|
||||||
|
}
|
||||||
|
echo '<HR>';
|
||||||
|
|
||||||
$tagwriter->tag_data = $TagData;
|
|
||||||
if ($tagwriter->WriteTags()) {
|
|
||||||
echo 'Successfully wrote tags<BR>';
|
|
||||||
if (!empty($tagwriter->warnings)) {
|
|
||||||
echo 'There were some warnings:<BLOCKQUOTE STYLE="background-color:#FFCC33; padding: 10px;">'.implode('<br><br>', $tagwriter->warnings).'</BLOCKQUOTE>';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo 'Failed to write tags!<BLOCKQUOTE STYLE="background-color:#FF9999; padding: 10px;">'.implode('<br><br>', $tagwriter->errors).'</BLOCKQUOTE>';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo 'WARNING: no tag formats selected for writing - nothing written';
|
|
||||||
}
|
|
||||||
echo '<HR>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
echo '<div style="font-size: 1.2em; font-weight: bold;">Sample tag editor/writer</div>';
|
echo '<div style="font-size: 1.2em; font-weight: bold;">Sample tag editor/writer</div>';
|
||||||
echo '<a href="'.htmlentities($browsescriptfilename.'?listdirectory='.rawurlencode(realpath(dirname($Filename))), ENT_QUOTES).'">Browse current directory</a><br>';
|
echo '<a href="'.htmlentities($browsescriptfilename.'?listdirectory='.rawurlencode(realpath(dirname($Filename))), ENT_QUOTES).'">Browse current directory</a><br>';
|
||||||
if (!empty($Filename)) {
|
if (!empty($Filename)) {
|
||||||
echo '<a href="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'">Start Over</a><br><br>';
|
echo '<a href="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'">Start Over</a><br><br>';
|
||||||
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'" method="post" enctype="multipart/form-data">';
|
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'" method="post" enctype="multipart/form-data">';
|
||||||
echo '<table border="3" cellspacing="0" cellpadding="4">';
|
echo '<table border="3" cellspacing="0" cellpadding="4">';
|
||||||
echo '<tr><th align="right">Filename:</th><td><input type="hidden" name="Filename" value="'.htmlentities($Filename, ENT_QUOTES).'"><a href="'.htmlentities($browsescriptfilename.'?filename='.rawurlencode($Filename), ENT_QUOTES).'" target="_blank">'.$Filename.'</a></td></tr>';
|
echo '<tr><th align="right">Filename:</th><td><input type="hidden" name="Filename" value="'.htmlentities($Filename, ENT_QUOTES).'"><a href="'.htmlentities($browsescriptfilename.'?filename='.rawurlencode($Filename), ENT_QUOTES).'" target="_blank">'.$Filename.'</a></td></tr>';
|
||||||
if (file_exists($Filename)) {
|
if (file_exists($Filename)) {
|
||||||
// Initialize getID3 engine
|
|
||||||
$getID3 = new getID3;
|
|
||||||
$OldThisFileInfo = $getID3->analyze($Filename);
|
|
||||||
getid3_lib::CopyTagsToComments($OldThisFileInfo);
|
|
||||||
|
|
||||||
switch ($OldThisFileInfo['fileformat']) {
|
// Initialize getID3 engine
|
||||||
case 'mp3':
|
$getID3 = new getID3;
|
||||||
case 'mp2':
|
$OldThisFileInfo = $getID3->analyze($Filename);
|
||||||
case 'mp1':
|
getid3_lib::CopyTagsToComments($OldThisFileInfo);
|
||||||
$ValidTagTypes = ['id3v1', 'id3v2.3', 'ape'];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mpc':
|
switch ($OldThisFileInfo['fileformat']) {
|
||||||
$ValidTagTypes = ['ape'];
|
case 'mp3':
|
||||||
break;
|
case 'mp2':
|
||||||
|
case 'mp1':
|
||||||
|
$ValidTagTypes = array('id3v1', 'id3v2.3', 'ape');
|
||||||
|
break;
|
||||||
|
|
||||||
case 'ogg':
|
case 'mpc':
|
||||||
if (!empty($OldThisFileInfo['audio']['dataformat']) && ($OldThisFileInfo['audio']['dataformat'] == 'flac')) {
|
$ValidTagTypes = array('ape');
|
||||||
//$ValidTagTypes = array('metaflac');
|
break;
|
||||||
// metaflac doesn't (yet) work with OggFLAC files
|
|
||||||
$ValidTagTypes = [];
|
|
||||||
} else {
|
|
||||||
$ValidTagTypes = ['vorbiscomment'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flac':
|
case 'ogg':
|
||||||
$ValidTagTypes = ['metaflac'];
|
if (!empty($OldThisFileInfo['audio']['dataformat']) && ($OldThisFileInfo['audio']['dataformat'] == 'flac')) {
|
||||||
break;
|
//$ValidTagTypes = array('metaflac');
|
||||||
|
// metaflac doesn't (yet) work with OggFLAC files
|
||||||
|
$ValidTagTypes = array();
|
||||||
|
} else {
|
||||||
|
$ValidTagTypes = array('vorbiscomment');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'real':
|
case 'flac':
|
||||||
$ValidTagTypes = ['real'];
|
$ValidTagTypes = array('metaflac');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case 'real':
|
||||||
$ValidTagTypes = [];
|
$ValidTagTypes = array('real');
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
echo '<tr><td align="right"><b>Title</b></td> <td><input type="text" size="40" name="Title" value="'.htmlentities((!empty($OldThisFileInfo['comments']['title']) ? implode(', ', $OldThisFileInfo['comments']['title']) : ''), ENT_QUOTES).'"></td></tr>';
|
|
||||||
echo '<tr><td align="right"><b>Artist</b></td><td><input type="text" size="40" name="Artist" value="'.htmlentities((!empty($OldThisFileInfo['comments']['artist']) ? implode(', ', $OldThisFileInfo['comments']['artist']) : ''), ENT_QUOTES).'"></td></tr>';
|
|
||||||
echo '<tr><td align="right"><b>Album</b></td> <td><input type="text" size="40" name="Album" value="'.htmlentities((!empty($OldThisFileInfo['comments']['album']) ? implode(', ', $OldThisFileInfo['comments']['album']) : ''), ENT_QUOTES).'"></td></tr>';
|
|
||||||
echo '<tr><td align="right"><b>Year</b></td> <td><input type="text" size="4" name="Year" value="'.htmlentities((!empty($OldThisFileInfo['comments']['year']) ? implode(', ', $OldThisFileInfo['comments']['year']) : ''), ENT_QUOTES).'"></td></tr>';
|
|
||||||
|
|
||||||
$TracksTotal = '';
|
default:
|
||||||
$TrackNumber = '';
|
$ValidTagTypes = array();
|
||||||
if (!empty($OldThisFileInfo['comments']['track_number']) && is_array($OldThisFileInfo['comments']['track_number'])) {
|
break;
|
||||||
$RawTrackNumberArray = $OldThisFileInfo['comments']['track_number'];
|
}
|
||||||
} elseif (!empty($OldThisFileInfo['comments']['track']) && is_array($OldThisFileInfo['comments']['track'])) {
|
echo '<tr><td align="right"><b>Title</b></td> <td><input type="text" size="40" name="Title" value="'.htmlentities((!empty($OldThisFileInfo['comments']['title']) ? implode(', ', $OldThisFileInfo['comments']['title'] ) : ''), ENT_QUOTES).'"></td></tr>';
|
||||||
$RawTrackNumberArray = $OldThisFileInfo['comments']['track'];
|
echo '<tr><td align="right"><b>Artist</b></td><td><input type="text" size="40" name="Artist" value="'.htmlentities((!empty($OldThisFileInfo['comments']['artist']) ? implode(', ', $OldThisFileInfo['comments']['artist']) : ''), ENT_QUOTES).'"></td></tr>';
|
||||||
} else {
|
echo '<tr><td align="right"><b>Album</b></td> <td><input type="text" size="40" name="Album" value="'.htmlentities((!empty($OldThisFileInfo['comments']['album']) ? implode(', ', $OldThisFileInfo['comments']['album'] ) : ''), ENT_QUOTES).'"></td></tr>';
|
||||||
$RawTrackNumberArray = [];
|
echo '<tr><td align="right"><b>Year</b></td> <td><input type="text" size="4" name="Year" value="'.htmlentities((!empty($OldThisFileInfo['comments']['year']) ? implode(', ', $OldThisFileInfo['comments']['year'] ) : ''), ENT_QUOTES).'"></td></tr>';
|
||||||
}
|
|
||||||
foreach ($RawTrackNumberArray as $key => $value) {
|
|
||||||
if (strlen($value) > strlen($TrackNumber)) {
|
|
||||||
// ID3v1 may store track as "3" but ID3v2/APE would store as "03/16"
|
|
||||||
$TrackNumber = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strstr($TrackNumber, '/')) {
|
|
||||||
list($TrackNumber, $TracksTotal) = explode('/', $TrackNumber);
|
|
||||||
}
|
|
||||||
echo '<tr><td align="right"><b>Track</b></td><td><input type="text" size="2" name="Track" value="'.htmlentities($TrackNumber, ENT_QUOTES).'"> of <input type="text" size="2" name="TracksTotal" value="'.htmlentities($TracksTotal, ENT_QUOTES).'"></TD></TR>';
|
|
||||||
|
|
||||||
$ArrayOfGenresTemp = getid3_id3v1::ArrayOfGenres(); // get the array of genres
|
$TracksTotal = '';
|
||||||
foreach ($ArrayOfGenresTemp as $key => $value) { // change keys to match displayed value
|
$TrackNumber = '';
|
||||||
$ArrayOfGenres[$value] = $value;
|
if (!empty($OldThisFileInfo['comments']['track_number']) && is_array($OldThisFileInfo['comments']['track_number'])) {
|
||||||
}
|
$RawTrackNumberArray = $OldThisFileInfo['comments']['track_number'];
|
||||||
unset($ArrayOfGenresTemp); // remove temporary array
|
} elseif (!empty($OldThisFileInfo['comments']['track']) && is_array($OldThisFileInfo['comments']['track'])) {
|
||||||
unset($ArrayOfGenres['Cover']); // take off these special cases
|
$RawTrackNumberArray = $OldThisFileInfo['comments']['track'];
|
||||||
unset($ArrayOfGenres['Remix']);
|
} else {
|
||||||
unset($ArrayOfGenres['Unknown']);
|
$RawTrackNumberArray = array();
|
||||||
$ArrayOfGenres[''] = '- Unknown -'; // Add special cases back in with renamed key/value
|
}
|
||||||
$ArrayOfGenres['Cover'] = '-Cover-';
|
foreach ($RawTrackNumberArray as $key => $value) {
|
||||||
$ArrayOfGenres['Remix'] = '-Remix-';
|
if (strlen($value) > strlen($TrackNumber)) {
|
||||||
asort($ArrayOfGenres); // sort into alphabetical order
|
// ID3v1 may store track as "3" but ID3v2/APE would store as "03/16"
|
||||||
echo '<tr><th align="right">Genre</th><td><select name="Genre">';
|
$TrackNumber = $value;
|
||||||
$AllGenresArray = (!empty($OldThisFileInfo['comments']['genre']) ? $OldThisFileInfo['comments']['genre'] : []);
|
}
|
||||||
foreach ($ArrayOfGenres as $key => $value) {
|
}
|
||||||
echo '<option value="'.htmlentities($key, ENT_QUOTES).'"';
|
if (strstr($TrackNumber, '/')) {
|
||||||
if (in_array($key, $AllGenresArray)) {
|
list($TrackNumber, $TracksTotal) = explode('/', $TrackNumber);
|
||||||
echo ' selected="selected"';
|
}
|
||||||
unset($AllGenresArray[array_search($key, $AllGenresArray)]);
|
echo '<tr><td align="right"><b>Track</b></td><td><input type="text" size="2" name="Track" value="'.htmlentities($TrackNumber, ENT_QUOTES).'"> of <input type="text" size="2" name="TracksTotal" value="'.htmlentities($TracksTotal, ENT_QUOTES).'"></TD></TR>';
|
||||||
sort($AllGenresArray);
|
|
||||||
}
|
|
||||||
echo '>'.htmlentities($value).'</option>';
|
|
||||||
}
|
|
||||||
echo '</select><input type="text" name="GenreOther" size="10" value="'.htmlentities((!empty($AllGenresArray[0]) ? $AllGenresArray[0] : ''), ENT_QUOTES).'"></td></tr>';
|
|
||||||
|
|
||||||
echo '<tr><td align="right"><b>Write Tags</b></td><td>';
|
$ArrayOfGenresTemp = getid3_id3v1::ArrayOfGenres(); // get the array of genres
|
||||||
foreach ($ValidTagTypes as $ValidTagType) {
|
foreach ($ArrayOfGenresTemp as $key => $value) { // change keys to match displayed value
|
||||||
echo '<input type="checkbox" name="TagFormatsToWrite[]" value="'.$ValidTagType.'"';
|
$ArrayOfGenres[$value] = $value;
|
||||||
if (count($ValidTagTypes) == 1) {
|
}
|
||||||
echo ' checked="checked"';
|
unset($ArrayOfGenresTemp); // remove temporary array
|
||||||
} else {
|
unset($ArrayOfGenres['Cover']); // take off these special cases
|
||||||
switch ($ValidTagType) {
|
unset($ArrayOfGenres['Remix']);
|
||||||
case 'id3v2.2':
|
unset($ArrayOfGenres['Unknown']);
|
||||||
case 'id3v2.3':
|
$ArrayOfGenres[''] = '- Unknown -'; // Add special cases back in with renamed key/value
|
||||||
case 'id3v2.4':
|
$ArrayOfGenres['Cover'] = '-Cover-';
|
||||||
if (isset($OldThisFileInfo['tags']['id3v2'])) {
|
$ArrayOfGenres['Remix'] = '-Remix-';
|
||||||
echo ' checked="checked"';
|
asort($ArrayOfGenres); // sort into alphabetical order
|
||||||
}
|
echo '<tr><th align="right">Genre</th><td><select name="Genre">';
|
||||||
break;
|
$AllGenresArray = (!empty($OldThisFileInfo['comments']['genre']) ? $OldThisFileInfo['comments']['genre'] : array());
|
||||||
|
foreach ($ArrayOfGenres as $key => $value) {
|
||||||
|
echo '<option value="'.htmlentities($key, ENT_QUOTES).'"';
|
||||||
|
if (in_array($key, $AllGenresArray)) {
|
||||||
|
echo ' selected="selected"';
|
||||||
|
unset($AllGenresArray[array_search($key, $AllGenresArray)]);
|
||||||
|
sort($AllGenresArray);
|
||||||
|
}
|
||||||
|
echo '>'.htmlentities($value).'</option>';
|
||||||
|
}
|
||||||
|
echo '</select><input type="text" name="GenreOther" size="10" value="'.htmlentities((!empty($AllGenresArray[0]) ? $AllGenresArray[0] : ''), ENT_QUOTES).'"></td></tr>';
|
||||||
|
|
||||||
default:
|
echo '<tr><td align="right"><b>Write Tags</b></td><td>';
|
||||||
if (isset($OldThisFileInfo['tags'][$ValidTagType])) {
|
foreach ($ValidTagTypes as $ValidTagType) {
|
||||||
echo ' checked="checked"';
|
echo '<input type="checkbox" name="TagFormatsToWrite[]" value="'.$ValidTagType.'"';
|
||||||
}
|
if (count($ValidTagTypes) == 1) {
|
||||||
break;
|
echo ' checked="checked"';
|
||||||
}
|
} else {
|
||||||
}
|
switch ($ValidTagType) {
|
||||||
echo '>'.$ValidTagType.'<br>';
|
case 'id3v2.2':
|
||||||
}
|
case 'id3v2.3':
|
||||||
if (count($ValidTagTypes) > 1) {
|
case 'id3v2.4':
|
||||||
echo '<hr><input type="checkbox" name="remove_other_tags" value="1"> Remove non-selected tag formats when writing new tag<br>';
|
if (isset($OldThisFileInfo['tags']['id3v2'])) {
|
||||||
}
|
echo ' checked="checked"';
|
||||||
echo '</td></tr>';
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
echo '<tr><td align="right"><b>Comment</b></td><td><textarea cols="30" rows="3" name="Comment" wrap="virtual">'.((isset($OldThisFileInfo['comments']['comment']) && is_array($OldThisFileInfo['comments']['comment'])) ? implode("\n", $OldThisFileInfo['comments']['comment']) : '').'</textarea></td></tr>';
|
default:
|
||||||
|
if (isset($OldThisFileInfo['tags'][$ValidTagType])) {
|
||||||
|
echo ' checked="checked"';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo '>'.$ValidTagType.'<br>';
|
||||||
|
}
|
||||||
|
if (count($ValidTagTypes) > 1) {
|
||||||
|
echo '<hr><input type="checkbox" name="remove_other_tags" value="1"> Remove non-selected tag formats when writing new tag<br>';
|
||||||
|
}
|
||||||
|
echo '</td></tr>';
|
||||||
|
|
||||||
|
echo '<tr><td align="right"><b>Comment</b></td><td><textarea cols="30" rows="3" name="Comment" wrap="virtual">'.((isset($OldThisFileInfo['comments']['comment']) && is_array($OldThisFileInfo['comments']['comment'])) ? implode("\n", $OldThisFileInfo['comments']['comment']) : '').'</textarea></td></tr>';
|
||||||
|
|
||||||
|
echo '<tr><td align="right"><b>Picture</b><br>(ID3v2 only)</td><td><input type="file" name="userfile" accept="image/jpeg, image/gif, image/png"><br>';
|
||||||
|
echo '<select name="APICpictureType">';
|
||||||
|
$APICtypes = getid3_id3v2::APICPictureTypeLookup('', true);
|
||||||
|
foreach ($APICtypes as $key => $value) {
|
||||||
|
echo '<option value="'.htmlentities($key, ENT_QUOTES).'">'.htmlentities($value).'</option>';
|
||||||
|
}
|
||||||
|
echo '</select></td></tr>';
|
||||||
|
echo '<tr><td align="center" colspan="2"><input type="submit" name="WriteTags" value="Save Changes"> ';
|
||||||
|
echo '<input type="reset" value="Reset"></td></tr>';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
echo '<tr><td align="right"><b>Error</b></td><td>'.htmlentities($Filename).' does not exist</td></tr>';
|
||||||
|
|
||||||
|
}
|
||||||
|
echo '</table>';
|
||||||
|
echo '</form>';
|
||||||
|
|
||||||
echo '<tr><td align="right"><b>Picture</b><br>(ID3v2 only)</td><td><input type="file" name="userfile" accept="image/jpeg, image/gif, image/png"><br>';
|
|
||||||
echo '<select name="APICpictureType">';
|
|
||||||
$APICtypes = getid3_id3v2::APICPictureTypeLookup('', true);
|
|
||||||
foreach ($APICtypes as $key => $value) {
|
|
||||||
echo '<option value="'.htmlentities($key, ENT_QUOTES).'">'.htmlentities($value).'</option>';
|
|
||||||
}
|
|
||||||
echo '</select></td></tr>';
|
|
||||||
echo '<tr><td align="center" colspan="2"><input type="submit" name="WriteTags" value="Save Changes"> ';
|
|
||||||
echo '<input type="reset" value="Reset"></td></tr>';
|
|
||||||
} else {
|
|
||||||
echo '<tr><td align="right"><b>Error</b></td><td>'.htmlentities($Filename).' does not exist</td></tr>';
|
|
||||||
}
|
|
||||||
echo '</table>';
|
|
||||||
echo '</form>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '</body></html>';
|
echo '</body></html>';
|
|
@ -13,90 +13,89 @@
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
function UnzipFileContents($filename, &$errors)
|
function UnzipFileContents($filename, &$errors) {
|
||||||
{
|
$errors = array();
|
||||||
$errors = [];
|
$DecompressedFileContents = array();
|
||||||
$DecompressedFileContents = [];
|
if (!class_exists('getID3')) {
|
||||||
if (!class_exists('getID3')) {
|
$errors[] = 'class getID3 not defined, please include getid3.php';
|
||||||
$errors[] = 'class getID3 not defined, please include getid3.php';
|
} elseif (include_once('module.archive.zip.php')) {
|
||||||
} elseif (include_once('module.archive.zip.php')) {
|
$getid3 = new getID3();
|
||||||
$getid3 = new getID3();
|
$getid3->info['filesize'] = filesize($filename);
|
||||||
$getid3->info['filesize'] = filesize($filename);
|
ob_start();
|
||||||
ob_start();
|
if ($getid3->fp = fopen($filename, 'rb')) {
|
||||||
if ($getid3->fp = fopen($filename, 'rb')) {
|
ob_end_clean();
|
||||||
ob_end_clean();
|
$getid3_zip = new getid3_zip($getid3);
|
||||||
$getid3_zip = new getid3_zip($getid3);
|
$getid3_zip->analyze($filename);
|
||||||
$getid3_zip->analyze($filename);
|
if (($getid3->info['fileformat'] == 'zip') && !empty($getid3->info['zip']['files'])) {
|
||||||
if (($getid3->info['fileformat'] == 'zip') && !empty($getid3->info['zip']['files'])) {
|
if (!empty($getid3->info['zip']['central_directory'])) {
|
||||||
if (!empty($getid3->info['zip']['central_directory'])) {
|
$ZipDirectoryToWalk = $getid3->info['zip']['central_directory'];
|
||||||
$ZipDirectoryToWalk = $getid3->info['zip']['central_directory'];
|
} elseif (!empty($getid3->info['zip']['entries'])) {
|
||||||
} elseif (!empty($getid3->info['zip']['entries'])) {
|
$ZipDirectoryToWalk = $getid3->info['zip']['entries'];
|
||||||
$ZipDirectoryToWalk = $getid3->info['zip']['entries'];
|
} else {
|
||||||
} else {
|
$errors[] = 'failed to parse ZIP attachment "'.$piece_filename.'" (no central directory)<br>';
|
||||||
$errors[] = 'failed to parse ZIP attachment "'.$piece_filename.'" (no central directory)<br>';
|
fclose($getid3->fp);
|
||||||
fclose($getid3->fp);
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
foreach ($ZipDirectoryToWalk as $key => $valuearray) {
|
||||||
foreach ($ZipDirectoryToWalk as $key => $valuearray) {
|
fseek($getid3->fp, $valuearray['entry_offset'], SEEK_SET);
|
||||||
fseek($getid3->fp, $valuearray['entry_offset'], SEEK_SET);
|
$LocalFileHeader = $getid3_zip->ZIPparseLocalFileHeader($getid3->fp);
|
||||||
$LocalFileHeader = $getid3_zip->ZIPparseLocalFileHeader($getid3->fp);
|
if ($LocalFileHeader['flags']['encrypted']) {
|
||||||
if ($LocalFileHeader['flags']['encrypted']) {
|
// password-protected
|
||||||
// password-protected
|
$DecompressedFileContents[$valuearray['filename']] = '';
|
||||||
$DecompressedFileContents[$valuearray['filename']] = '';
|
} else {
|
||||||
} else {
|
fseek($getid3->fp, $LocalFileHeader['data_offset'], SEEK_SET);
|
||||||
fseek($getid3->fp, $LocalFileHeader['data_offset'], SEEK_SET);
|
$compressedFileData = '';
|
||||||
$compressedFileData = '';
|
while ((strlen($compressedFileData) < $LocalFileHeader['compressed_size']) && !feof($getid3->fp)) {
|
||||||
while ((strlen($compressedFileData) < $LocalFileHeader['compressed_size']) && !feof($getid3->fp)) {
|
$compressedFileData .= fread($getid3->fp, 32768);
|
||||||
$compressedFileData .= fread($getid3->fp, 32768);
|
}
|
||||||
}
|
switch ($LocalFileHeader['raw']['compression_method']) {
|
||||||
switch ($LocalFileHeader['raw']['compression_method']) {
|
case 0: // store - great, just copy data unchanged
|
||||||
case 0: // store - great, just copy data unchanged
|
$uncompressedFileData = $compressedFileData;
|
||||||
$uncompressedFileData = $compressedFileData;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 8: // deflate
|
case 8: // deflate
|
||||||
ob_start();
|
ob_start();
|
||||||
$uncompressedFileData = gzinflate($compressedFileData);
|
$uncompressedFileData = gzinflate($compressedFileData);
|
||||||
$gzinflate_errors = trim(strip_tags(ob_get_contents()));
|
$gzinflate_errors = trim(strip_tags(ob_get_contents()));
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
if ($gzinflate_errors) {
|
if ($gzinflate_errors) {
|
||||||
$errors[] = 'gzinflate() failed for file ['.$LocalFileHeader['filename'].']: "'.$gzinflate_errors.'"';
|
$errors[] = 'gzinflate() failed for file ['.$LocalFileHeader['filename'].']: "'.$gzinflate_errors.'"';
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // shrink
|
case 1: // shrink
|
||||||
case 2: // reduce-1
|
case 2: // reduce-1
|
||||||
case 3: // reduce-2
|
case 3: // reduce-2
|
||||||
case 4: // reduce-3
|
case 4: // reduce-3
|
||||||
case 5: // reduce-4
|
case 5: // reduce-4
|
||||||
case 6: // implode
|
case 6: // implode
|
||||||
case 7: // tokenize
|
case 7: // tokenize
|
||||||
case 9: // deflate64
|
case 9: // deflate64
|
||||||
case 10: // PKWARE Date Compression Library Imploding
|
case 10: // PKWARE Date Compression Library Imploding
|
||||||
$DecompressedFileContents[$valuearray['filename']] = '';
|
$DecompressedFileContents[$valuearray['filename']] = '';
|
||||||
$errors[] = 'unsupported ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].' = '.$getid3_zip->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']).')';
|
$errors[] = 'unsupported ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].' = '.$getid3_zip->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']).')';
|
||||||
continue 2;
|
continue 2;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$DecompressedFileContents[$valuearray['filename']] = '';
|
$DecompressedFileContents[$valuearray['filename']] = '';
|
||||||
$errors[] = 'unknown ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].')';
|
$errors[] = 'unknown ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].')';
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
$DecompressedFileContents[$valuearray['filename']] = $uncompressedFileData;
|
$DecompressedFileContents[$valuearray['filename']] = $uncompressedFileData;
|
||||||
unset($compressedFileData);
|
unset($compressedFileData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$errors[] = $filename.' does not appear to be a zip file';
|
$errors[] = $filename.' does not appear to be a zip file';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$error_message = ob_get_contents();
|
$error_message = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$errors[] = 'failed to fopen('.$filename.', rb): '.$error_message;
|
$errors[] = 'failed to fopen('.$filename.', rb): '.$error_message;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$errors[] = 'failed to include_once(module.archive.zip.php)';
|
$errors[] = 'failed to include_once(module.archive.zip.php)';
|
||||||
}
|
}
|
||||||
return $DecompressedFileContents;
|
return $DecompressedFileContents;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,8 @@ $database['hide']=true;
|
||||||
|
|
||||||
*/
|
*/
|
||||||
$dir = $_SERVER['PWD'];
|
$dir = $_SERVER['PWD'];
|
||||||
$media = ['mp4', 'm4v', 'mov', 'mp3', 'm4a', 'jpg', 'png', 'gif'];
|
$media = array('mp4', 'm4v', 'mov', 'mp3', 'm4a', 'jpg', 'png', 'gif');
|
||||||
$database = [];
|
$database = array();
|
||||||
/**
|
/**
|
||||||
* configure the database bellow
|
* configure the database bellow
|
||||||
*/
|
*/
|
||||||
|
@ -81,184 +81,178 @@ require_once(dirname(__FILE__).'/getid3.php');
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class dirscan
|
class dirscan {
|
||||||
{
|
/**
|
||||||
/**
|
* type_brace() * Might not work on Solaris and other non GNU systems *
|
||||||
* type_brace() * Might not work on Solaris and other non GNU systems *
|
*
|
||||||
*
|
* Configures a filetype list for use with glob searches,
|
||||||
* Configures a filetype list for use with glob searches,
|
* will match uppercase or lowercase extensions only, no mixing
|
||||||
* will match uppercase or lowercase extensions only, no mixing
|
* @param string $dir directory to use
|
||||||
* @param string $dir directory to use
|
* @param mixed cvs list of extentions or an array
|
||||||
* @param mixed cvs list of extentions or an array
|
* @return string or null if checks fail
|
||||||
* @return string or null if checks fail
|
*/
|
||||||
*/
|
private function type_brace($dir, $search=array()) {
|
||||||
private function type_brace($dir, $search = [])
|
$dir = str_replace(array('///', '//'), array('/', '/'), $dir);
|
||||||
{
|
if (!is_dir($dir)) {
|
||||||
$dir = str_replace(['///', '//'], ['/', '/'], $dir);
|
return null;
|
||||||
if (!is_dir($dir)) {
|
}
|
||||||
return null;
|
if (!is_array($search)) {
|
||||||
}
|
$e = explode(',', $search);
|
||||||
if (!is_array($search)) {
|
} elseif (count($search) < 1) {
|
||||||
$e = explode(',', $search);
|
return null;
|
||||||
} elseif (count($search) < 1) {
|
} else {
|
||||||
return null;
|
$e = $search;
|
||||||
} else {
|
}
|
||||||
$e = $search;
|
$ext = array();
|
||||||
}
|
foreach ($e as $new) {
|
||||||
$ext = [];
|
$ext[] = strtolower(trim($new));
|
||||||
foreach ($e as $new) {
|
$ext[] = strtoupper(trim($new));
|
||||||
$ext[] = strtolower(trim($new));
|
}
|
||||||
$ext[] = strtoupper(trim($new));
|
$b = $dir.'/*.{'.implode(',', $ext).'}';
|
||||||
}
|
return $b;
|
||||||
$b = $dir.'/*.{'.implode(',', $ext).'}';
|
}
|
||||||
return $b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this function will search 4 levels deep for directories
|
* this function will search 4 levels deep for directories
|
||||||
* will return null on failure
|
* will return null on failure
|
||||||
* @param string $root
|
* @param string $root
|
||||||
* @return array return an array of dirs under root
|
* @return array return an array of dirs under root
|
||||||
* @todo figure out how to block tabo directories with ease
|
* @todo figure out how to block tabo directories with ease
|
||||||
*/
|
*/
|
||||||
private function getDirs($root)
|
private function getDirs($root) {
|
||||||
{
|
switch ($root) { // return null on tabo directories, add as needed -> case {dir to block }: this is not perfect yet
|
||||||
switch ($root) { // return null on tabo directories, add as needed -> case {dir to block }: this is not perfect yet
|
case '/':
|
||||||
case '/':
|
case '/var':
|
||||||
case '/var':
|
case '/etc':
|
||||||
case '/etc':
|
case '/home':
|
||||||
case '/home':
|
case '/usr':
|
||||||
case '/usr':
|
case '/root':
|
||||||
case '/root':
|
case '/private/etc':
|
||||||
case '/private/etc':
|
case '/private/var':
|
||||||
case '/private/var':
|
case '/etc/apache2':
|
||||||
case '/etc/apache2':
|
case '/home':
|
||||||
case '/home':
|
case '/tmp':
|
||||||
case '/tmp':
|
case '/var/log':
|
||||||
case '/var/log':
|
return null;
|
||||||
return null;
|
break;
|
||||||
break;
|
default: // scan 4 directories deep
|
||||||
default: // scan 4 directories deep
|
if (!is_dir($root)) {
|
||||||
if (!is_dir($root)) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
$dirs = array_merge(glob($root.'/*', GLOB_ONLYDIR), glob($root.'/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*/*', GLOB_ONLYDIR));
|
||||||
$dirs = array_merge(glob($root.'/*', GLOB_ONLYDIR), glob($root.'/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*/*', GLOB_ONLYDIR));
|
break;
|
||||||
break;
|
}
|
||||||
}
|
if (count($dirs) < 1) {
|
||||||
if (count($dirs) < 1) {
|
$dirs = array($root);
|
||||||
$dirs = [$root];
|
}
|
||||||
}
|
return $dirs;
|
||||||
return $dirs;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* file_check() check the number of file that are found that match the brace search
|
* file_check() check the number of file that are found that match the brace search
|
||||||
*
|
*
|
||||||
* @param string $search
|
* @param string $search
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
private function file_check($search)
|
private function file_check($search) {
|
||||||
{
|
$t = array();
|
||||||
$t = [];
|
$s = glob($search, GLOB_BRACE);
|
||||||
$s = glob($search, GLOB_BRACE);
|
foreach ($s as $file) {
|
||||||
foreach ($s as $file) {
|
$t[] = str_replace(array('///', '//'), array('/', '/'), $file);
|
||||||
$t[] = str_replace(['///', '//'], ['/', '/'], $file);
|
}
|
||||||
}
|
if (count($t) > 0) {
|
||||||
if (count($t) > 0) {
|
return $t;
|
||||||
return $t;
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getTime()
|
function getTime() {
|
||||||
{
|
return microtime(true);
|
||||||
return microtime(true);
|
// old method for PHP < 5
|
||||||
// old method for PHP < 5
|
//$a = explode(' ', microtime());
|
||||||
//$a = explode(' ', microtime());
|
//return (double) $a[0] + $a[1];
|
||||||
//return (double) $a[0] + $a[1];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param type $dir
|
* @param type $dir
|
||||||
* @param type $match search type name extentions, can be an array or csv list
|
* @param type $match search type name extentions, can be an array or csv list
|
||||||
* @param type $cache caching extention, select one of sqlite3, mysql, dbm
|
* @param type $cache caching extention, select one of sqlite3, mysql, dbm
|
||||||
* @param array $opt database options,
|
* @param array $opt database options,
|
||||||
*/
|
*/
|
||||||
function scan_files($dir, $match, $cache = 'sqlite3', $opt = ['table'=>'getid3_cache', 'hide'=>true])
|
function scan_files($dir, $match, $cache='sqlite3', $opt=array('table'=>'getid3_cache', 'hide'=>true)) {
|
||||||
{
|
$Start = self::getTime();
|
||||||
$Start = self::getTime();
|
switch ($cache) { // load the caching module
|
||||||
switch ($cache) { // load the caching module
|
case 'sqlite3':
|
||||||
case 'sqlite3':
|
if (!class_exists('getID3_cached_sqlite3')) {
|
||||||
if (!class_exists('getID3_cached_sqlite3')) {
|
require_once(dirname(__FILE__)).'/extension.cache.sqlite3.php';
|
||||||
require_once(dirname(__FILE__)).'/extension.cache.sqlite3.php';
|
}
|
||||||
}
|
$id3 = new getID3_cached_sqlite3($opt['table'], $opt['hide']);
|
||||||
$id3 = new getID3_cached_sqlite3($opt['table'], $opt['hide']);
|
break;
|
||||||
break;
|
case 'mysql':
|
||||||
case 'mysql':
|
if (!class_exists('getID3_cached_mysql')) {
|
||||||
if (!class_exists('getID3_cached_mysql')) {
|
require_once(dirname(__FILE__)).'/extension.cache.mysql.php';
|
||||||
require_once(dirname(__FILE__)).'/extension.cache.mysql.php';
|
}
|
||||||
}
|
$id3 = new getID3_cached_mysql($opt['host'], $opt['database'], $opt['username'], $opt['password'], $opt['table']);
|
||||||
$id3 = new getID3_cached_mysql($opt['host'], $opt['database'], $opt['username'], $opt['password'], $opt['table']);
|
break;
|
||||||
break;
|
// I'll leave this for some one else
|
||||||
// I'll leave this for some one else
|
//case 'dbm':
|
||||||
//case 'dbm':
|
// if (!class_exists('getID3_cached_dbm')) {
|
||||||
// if (!class_exists('getID3_cached_dbm')) {
|
// require_once(dirname(__FILE__)).'/extension.cache.dbm.php';
|
||||||
// require_once(dirname(__FILE__)).'/extension.cache.dbm.php';
|
// }
|
||||||
// }
|
// die(' This has not be implemented, sorry for the inconvenience');
|
||||||
// die(' This has not be implemented, sorry for the inconvenience');
|
// break;
|
||||||
// break;
|
default:
|
||||||
default:
|
die(' You have selected an Invalid cache type, only "sqlite3" and "mysql" are valid'."\n");
|
||||||
die(' You have selected an Invalid cache type, only "sqlite3" and "mysql" are valid'."\n");
|
break;
|
||||||
break;
|
}
|
||||||
}
|
$count = array('dir'=>0, 'file'=>0);
|
||||||
$count = ['dir'=>0, 'file'=>0];
|
$dirs = self::getDirs($dir);
|
||||||
$dirs = self::getDirs($dir);
|
if ($dirs !== null) {
|
||||||
if ($dirs !== null) {
|
foreach ($dirs as $d) {
|
||||||
foreach ($dirs as $d) {
|
echo ' Scanning: '.$d."\n";
|
||||||
echo ' Scanning: '.$d."\n";
|
$search = self::type_brace($d, $match);
|
||||||
$search = self::type_brace($d, $match);
|
if ($search !== null) {
|
||||||
if ($search !== null) {
|
$files = self::file_check($search);
|
||||||
$files = self::file_check($search);
|
if ($files !== null) {
|
||||||
if ($files !== null) {
|
foreach ($files as $f) {
|
||||||
foreach ($files as $f) {
|
echo ' * Analyzing '.$f.' '."\n";
|
||||||
echo ' * Analyzing '.$f.' '."\n";
|
$id3->analyze($f);
|
||||||
$id3->analyze($f);
|
$count['file']++;
|
||||||
$count['file']++;
|
}
|
||||||
}
|
$count['dir']++;
|
||||||
$count['dir']++;
|
} else {
|
||||||
} else {
|
echo 'Failed to get files '."\n";
|
||||||
echo 'Failed to get files '."\n";
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
echo 'Failed to create match string '."\n";
|
||||||
echo 'Failed to create match string '."\n";
|
}
|
||||||
}
|
}
|
||||||
}
|
echo '**************************************'."\n";
|
||||||
echo '**************************************'."\n";
|
echo '* Finished Scanning your directories '."\n*\n";
|
||||||
echo '* Finished Scanning your directories '."\n*\n";
|
echo '* Directories '.$count['dir']."\n";
|
||||||
echo '* Directories '.$count['dir']."\n";
|
echo '* Files '.$count['file']."\n";
|
||||||
echo '* Files '.$count['file']."\n";
|
$End = self::getTime();
|
||||||
$End = self::getTime();
|
$t = number_format(($End - $Start) / 60, 2);
|
||||||
$t = number_format(($End - $Start) / 60, 2);
|
echo '* Time taken to scan '.$dir.' '.$t.' min '."\n";
|
||||||
echo '* Time taken to scan '.$dir.' '.$t.' min '."\n";
|
echo '**************************************'."\n";
|
||||||
echo '**************************************'."\n";
|
} else {
|
||||||
} else {
|
echo ' failed to get directories '."\n";
|
||||||
echo ' failed to get directories '."\n";
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PHP_SAPI === 'cli') {
|
if (PHP_SAPI === 'cli') {
|
||||||
if (count($argv) == 2) {
|
if (count($argv) == 2) {
|
||||||
if (is_dir($argv[1])) {
|
if (is_dir($argv[1])) {
|
||||||
$dir = $argv[1];
|
$dir = $argv[1];
|
||||||
}
|
}
|
||||||
if (count(explode(',', $argv[2])) > 0) {
|
if (count(explode(',', $argv[2])) > 0) {
|
||||||
$media = $arg[2];
|
$media = $arg[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo ' * Starting to scan directory: '.$dir."\n";
|
echo ' * Starting to scan directory: '.$dir."\n";
|
||||||
echo ' * Using default media types: '.implode(',', $media)."\n";
|
echo ' * Using default media types: '.implode(',', $media)."\n";
|
||||||
dirscan::scan_files($dir, $media, $cache, $database);
|
dirscan::scan_files($dir, $media, $cache, $database);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ If you don't know what to run, take a look at <a href="demo.browse.php"><b>demo.
|
||||||
Other demos:<ul>
|
Other demos:<ul>
|
||||||
<?php
|
<?php
|
||||||
if ($dh = opendir('.')) {
|
if ($dh = opendir('.')) {
|
||||||
while ($file = readdir($dh)) {
|
while ($file = readdir($dh)) {
|
||||||
if (preg_match('#^demo\\..+\\.php$#', $file)) {
|
if (preg_match('#^demo\\..+\\.php$#', $file)) {
|
||||||
echo '<li><a href="'.htmlentities($file, ENT_QUOTES).'">'.htmlentities($file).'</a></li>';
|
echo '<li><a href="'.htmlentities($file, ENT_QUOTES).'">'.htmlentities($file).'</a></li>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -73,138 +73,137 @@
|
||||||
class getID3_cached_dbm extends getID3
|
class getID3_cached_dbm extends getID3
|
||||||
{
|
{
|
||||||
|
|
||||||
// public: constructor - see top of this file for cache type and cache_options
|
// public: constructor - see top of this file for cache type and cache_options
|
||||||
public function __construct($cache_type, $dbm_filename, $lock_filename)
|
public function __construct($cache_type, $dbm_filename, $lock_filename) {
|
||||||
{
|
|
||||||
|
|
||||||
// Check for dba extension
|
// Check for dba extension
|
||||||
if (!extension_loaded('dba')) {
|
if (!extension_loaded('dba')) {
|
||||||
throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.');
|
throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for specific dba driver
|
// Check for specific dba driver
|
||||||
if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) {
|
if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) {
|
||||||
throw new Exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
|
throw new Exception('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)) {
|
||||||
throw new Exception('failed to create lock file: '.$lock_filename);
|
throw new Exception('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)) {
|
||||||
throw new Exception('lock file: '.$lock_filename.' is not writable');
|
throw new Exception('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)) {
|
||||||
throw new Exception('failed to create dbm file: '.$dbm_filename);
|
throw new Exception('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
|
|
||||||
$this->dba = dba_open($dbm_filename, 'n', $cache_type);
|
|
||||||
|
|
||||||
if (!$this->dba) {
|
// Failed - create new dbm file
|
||||||
throw new Exception('failed to create dbm file: '.$dbm_filename);
|
$this->dba = dba_open($dbm_filename, 'n', $cache_type);
|
||||||
}
|
|
||||||
|
|
||||||
// Insert getID3 version number
|
if (!$this->dba) {
|
||||||
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
throw new Exception('failed to create dbm file: '.$dbm_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init misc values
|
// Insert getID3 version number
|
||||||
$this->cache_type = $cache_type;
|
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
||||||
$this->dbm_filename = $dbm_filename;
|
}
|
||||||
|
|
||||||
// Register destructor
|
// Init misc values
|
||||||
register_shutdown_function([$this, '__destruct']);
|
$this->cache_type = $cache_type;
|
||||||
|
$this->dbm_filename = $dbm_filename;
|
||||||
|
|
||||||
// Check version number and clear cache if changed
|
// Register destructor
|
||||||
if (dba_fetch(getID3::VERSION, $this->dba) != getID3::VERSION) {
|
register_shutdown_function(array($this, '__destruct'));
|
||||||
$this->clear_cache();
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct();
|
// Check version number and clear cache if changed
|
||||||
}
|
if (dba_fetch(getID3::VERSION, $this->dba) != getID3::VERSION) {
|
||||||
|
$this->clear_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// public: destructor
|
// public: destructor
|
||||||
public function __destruct()
|
public 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: clear cache
|
// public: clear cache
|
||||||
public function clear_cache()
|
public 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) {
|
||||||
throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
|
throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert getID3 version number
|
// Insert getID3 version number
|
||||||
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
|
||||||
|
|
||||||
// Re-register shutdown function
|
// Re-register shutdown function
|
||||||
register_shutdown_function([$this, '__destruct']);
|
register_shutdown_function(array($this, '__destruct'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// public: analyze file
|
// public: analyze file
|
||||||
public function analyze($filename)
|
public function analyze($filename) {
|
||||||
{
|
|
||||||
|
|
||||||
if (file_exists($filename)) {
|
if (file_exists($filename)) {
|
||||||
// Calc key filename::mod_time::size - should be unique
|
|
||||||
$key = $filename.'::'.filemtime($filename).'::'.filesize($filename);
|
|
||||||
|
|
||||||
// Loopup key
|
// Calc key filename::mod_time::size - should be unique
|
||||||
$result = dba_fetch($key, $this->dba);
|
$key = $filename.'::'.filemtime($filename).'::'.filesize($filename);
|
||||||
|
|
||||||
// Hit
|
// Loopup key
|
||||||
if ($result !== false) {
|
$result = dba_fetch($key, $this->dba);
|
||||||
return unserialize($result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Miss
|
// Hit
|
||||||
$result = parent::analyze($filename);
|
if ($result !== false) {
|
||||||
|
return unserialize($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save result
|
// Miss
|
||||||
if (file_exists($filename)) {
|
$result = parent::analyze($filename);
|
||||||
dba_insert($key, serialize($result), $this->dba);
|
|
||||||
}
|
// Save result
|
||||||
|
if (file_exists($filename)) {
|
||||||
|
dba_insert($key, serialize($result), $this->dba);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,120 +74,117 @@
|
||||||
class getID3_cached_mysql extends getID3
|
class getID3_cached_mysql extends getID3
|
||||||
{
|
{
|
||||||
|
|
||||||
// private vars
|
// private vars
|
||||||
private $cursor;
|
private $cursor;
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
|
|
||||||
// public: constructor - see top of this file for cache type and cache_options
|
// public: constructor - see top of this file for cache type and cache_options
|
||||||
public function __construct($host, $database, $username, $password, $table = 'getid3_cache')
|
public function __construct($host, $database, $username, $password, $table='getid3_cache') {
|
||||||
{
|
|
||||||
|
|
||||||
// Check for mysql support
|
// Check for mysql support
|
||||||
if (!function_exists('mysql_pconnect')) {
|
if (!function_exists('mysql_pconnect')) {
|
||||||
throw new Exception('PHP not compiled with mysql support.');
|
throw new Exception('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 Exception('mysql_pconnect() failed - check permissions and spelling.');
|
throw new Exception('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 Exception('Cannot use database '.$database);
|
throw new Exception('Cannot use database '.$database);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set table
|
// Set table
|
||||||
$this->table = $table;
|
$this->table = $table;
|
||||||
|
|
||||||
// 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
|
||||||
$version = '';
|
$version = '';
|
||||||
$SQLquery = 'SELECT `value`';
|
$SQLquery = 'SELECT `value`';
|
||||||
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
|
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
|
||||||
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string(getID3::VERSION).'\')';
|
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string(getID3::VERSION).'\')';
|
||||||
$SQLquery .= ' AND (`filesize` = -1)';
|
$SQLquery .= ' AND (`filesize` = -1)';
|
||||||
$SQLquery .= ' AND (`filetime` = -1)';
|
$SQLquery .= ' AND (`filetime` = -1)';
|
||||||
$SQLquery .= ' AND (`analyzetime` = -1)';
|
$SQLquery .= ' AND (`analyzetime` = -1)';
|
||||||
if ($this->cursor = mysql_query($SQLquery, $this->connection)) {
|
if ($this->cursor = mysql_query($SQLquery, $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::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// public: clear cache
|
// public: clear cache
|
||||||
public function clear_cache()
|
public function clear_cache() {
|
||||||
{
|
|
||||||
|
|
||||||
$this->cursor = mysql_query('DELETE FROM `'.mysql_real_escape_string($this->table).'`', $this->connection);
|
$this->cursor = mysql_query('DELETE FROM `'.mysql_real_escape_string($this->table).'`', $this->connection);
|
||||||
$this->cursor = mysql_query('INSERT INTO `'.mysql_real_escape_string($this->table).'` VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')', $this->connection);
|
$this->cursor = mysql_query('INSERT INTO `'.mysql_real_escape_string($this->table).'` VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')', $this->connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// public: analyze file
|
// public: analyze file
|
||||||
public function analyze($filename, $filesize = null, $original_filename = '')
|
public function analyze($filename, $filesize=null, $original_filename='') {
|
||||||
{
|
|
||||||
|
|
||||||
if (file_exists($filename)) {
|
if (file_exists($filename)) {
|
||||||
// Short-hands
|
|
||||||
$filetime = filemtime($filename);
|
|
||||||
$filesize = filesize($filename);
|
|
||||||
|
|
||||||
// Lookup file
|
// Short-hands
|
||||||
$SQLquery = 'SELECT `value`';
|
$filetime = filemtime($filename);
|
||||||
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
|
$filesize = filesize($filename);
|
||||||
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string($filename).'\')';
|
|
||||||
$SQLquery .= ' AND (`filesize` = \''.mysql_real_escape_string($filesize).'\')';
|
|
||||||
$SQLquery .= ' AND (`filetime` = \''.mysql_real_escape_string($filetime).'\')';
|
|
||||||
$this->cursor = mysql_query($SQLquery, $this->connection);
|
|
||||||
if (mysql_num_rows($this->cursor) > 0) {
|
|
||||||
// Hit
|
|
||||||
list($result) = mysql_fetch_array($this->cursor);
|
|
||||||
return unserialize(base64_decode($result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Miss
|
// Lookup file
|
||||||
$analysis = parent::analyze($filename, $filesize, $original_filename);
|
$SQLquery = 'SELECT `value`';
|
||||||
|
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
|
||||||
|
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string($filename).'\')';
|
||||||
|
$SQLquery .= ' AND (`filesize` = \''.mysql_real_escape_string($filesize).'\')';
|
||||||
|
$SQLquery .= ' AND (`filetime` = \''.mysql_real_escape_string($filetime).'\')';
|
||||||
|
$this->cursor = mysql_query($SQLquery, $this->connection);
|
||||||
|
if (mysql_num_rows($this->cursor) > 0) {
|
||||||
|
// Hit
|
||||||
|
list($result) = mysql_fetch_array($this->cursor);
|
||||||
|
return unserialize(base64_decode($result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save result
|
// Miss
|
||||||
if (file_exists($filename)) {
|
$analysis = parent::analyze($filename, $filesize, $original_filename);
|
||||||
$SQLquery = 'INSERT INTO `'.mysql_real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
|
|
||||||
$SQLquery .= '\''.mysql_real_escape_string($filename).'\'';
|
// Save result
|
||||||
$SQLquery .= ', \''.mysql_real_escape_string($filesize).'\'';
|
if (file_exists($filename)) {
|
||||||
$SQLquery .= ', \''.mysql_real_escape_string($filetime).'\'';
|
$SQLquery = 'INSERT INTO `'.mysql_real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
|
||||||
$SQLquery .= ', \''.mysql_real_escape_string(time()).'\'';
|
$SQLquery .= '\''.mysql_real_escape_string($filename).'\'';
|
||||||
$SQLquery .= ', \''.mysql_real_escape_string(base64_encode(serialize($analysis))).'\')';
|
$SQLquery .= ', \''.mysql_real_escape_string($filesize).'\'';
|
||||||
$this->cursor = mysql_query($SQLquery, $this->connection);
|
$SQLquery .= ', \''.mysql_real_escape_string($filetime).'\'';
|
||||||
}
|
$SQLquery .= ', \''.mysql_real_escape_string(time() ).'\'';
|
||||||
return $analysis;
|
$SQLquery .= ', \''.mysql_real_escape_string(base64_encode(serialize($analysis))).'\')';
|
||||||
}
|
$this->cursor = mysql_query($SQLquery, $this->connection);
|
||||||
|
}
|
||||||
|
return $analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// private: (re)create sql table
|
// private: (re)create sql table
|
||||||
private function create_table($drop = false)
|
private function create_table($drop=false) {
|
||||||
{
|
|
||||||
|
|
||||||
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` (';
|
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` (';
|
||||||
$SQLquery .= '`filename` VARCHAR(500) NOT NULL DEFAULT \'\'';
|
$SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
|
||||||
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
|
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
|
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
|
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
$SQLquery .= ', `value` LONGTEXT NOT NULL';
|
$SQLquery .= ', `value` LONGTEXT NOT NULL';
|
||||||
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM';
|
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci';
|
||||||
$this->cursor = mysql_query($SQLquery, $this->connection);
|
$this->cursor = mysql_query($SQLquery, $this->connection);
|
||||||
echo mysql_error($this->connection);
|
echo mysql_error($this->connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
183
app/Library/getid3/getid3/extension.cache.mysqli.php
Normal file
183
app/Library/getid3/getid3/extension.cache.mysqli.php
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
/// getID3() by James Heinrich <info@getid3.org> //
|
||||||
|
// available at http://getid3.sourceforge.net //
|
||||||
|
// or http://www.getid3.org //
|
||||||
|
// also https://github.com/JamesHeinrich/getID3 //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// extension.cache.mysqli.php - part of getID3() //
|
||||||
|
// Please see readme.txt for more information //
|
||||||
|
// ///
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// This extension written by Allan Hansen <ahØartemis*dk> //
|
||||||
|
// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> //
|
||||||
|
// ///
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a caching extension for getID3(). It works the exact same
|
||||||
|
* way as the getID3 class, but return cached information very fast
|
||||||
|
*
|
||||||
|
* Example: (see also demo.cache.mysql.php in /demo/)
|
||||||
|
*
|
||||||
|
* Normal getID3 usage (example):
|
||||||
|
*
|
||||||
|
* require_once 'getid3/getid3.php';
|
||||||
|
* $getID3 = new getID3;
|
||||||
|
* $getID3->encoding = 'UTF-8';
|
||||||
|
* $info1 = $getID3->analyze('file1.flac');
|
||||||
|
* $info2 = $getID3->analyze('file2.wv');
|
||||||
|
*
|
||||||
|
* getID3_cached usage:
|
||||||
|
*
|
||||||
|
* require_once 'getid3/getid3.php';
|
||||||
|
* require_once 'getid3/getid3/extension.cache.mysqli.php';
|
||||||
|
* // 5th parameter (tablename) is optional, default is 'getid3_cache'
|
||||||
|
* $getID3 = new getID3_cached_mysqli('localhost', 'database', 'username', 'password', 'tablename');
|
||||||
|
* $getID3->encoding = 'UTF-8';
|
||||||
|
* $info1 = $getID3->analyze('file1.flac');
|
||||||
|
* $info2 = $getID3->analyze('file2.wv');
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Supported Cache Types (this extension)
|
||||||
|
*
|
||||||
|
* SQL Databases:
|
||||||
|
*
|
||||||
|
* cache_type cache_options
|
||||||
|
* -------------------------------------------------------------------
|
||||||
|
* mysqli host, database, username, password
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* DBM-Style Databases: (use extension.cache.dbm)
|
||||||
|
*
|
||||||
|
* cache_type cache_options
|
||||||
|
* -------------------------------------------------------------------
|
||||||
|
* gdbm dbm_filename, lock_filename
|
||||||
|
* ndbm dbm_filename, lock_filename
|
||||||
|
* db2 dbm_filename, lock_filename
|
||||||
|
* db3 dbm_filename, lock_filename
|
||||||
|
* db4 dbm_filename, lock_filename (PHP5 required)
|
||||||
|
*
|
||||||
|
* PHP must have write access to both dbm_filename and lock_filename.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Recommended Cache Types
|
||||||
|
*
|
||||||
|
* Infrequent updates, many reads any DBM
|
||||||
|
* Frequent updates mysqli
|
||||||
|
*/
|
||||||
|
|
||||||
|
class getID3_cached_mysqli extends getID3
|
||||||
|
{
|
||||||
|
// private vars
|
||||||
|
private $mysqli;
|
||||||
|
private $cursor;
|
||||||
|
|
||||||
|
|
||||||
|
// public: constructor - see top of this file for cache type and cache_options
|
||||||
|
public function __construct($host, $database, $username, $password, $table='getid3_cache') {
|
||||||
|
|
||||||
|
// Check for mysqli support
|
||||||
|
if (!function_exists('mysqli_connect')) {
|
||||||
|
throw new Exception('PHP not compiled with mysqli support.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to database
|
||||||
|
$this->mysqli = new mysqli($host, $username, $password);
|
||||||
|
if (!$this->mysqli) {
|
||||||
|
throw new Exception('mysqli_connect() failed - check permissions and spelling.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select database
|
||||||
|
if (!$this->mysqli->select_db($database)) {
|
||||||
|
throw new Exception('Cannot use database '.$database);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set table
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
// Create cache table if not exists
|
||||||
|
$this->create_table();
|
||||||
|
|
||||||
|
// Check version number and clear cache if changed
|
||||||
|
$version = '';
|
||||||
|
$SQLquery = 'SELECT `value`';
|
||||||
|
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
|
||||||
|
$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string(getID3::VERSION).'\')';
|
||||||
|
$SQLquery .= ' AND (`filesize` = -1)';
|
||||||
|
$SQLquery .= ' AND (`filetime` = -1)';
|
||||||
|
$SQLquery .= ' AND (`analyzetime` = -1)';
|
||||||
|
if ($this->cursor = $this->mysqli->query($SQLquery)) {
|
||||||
|
list($version) = $this->cursor->fetch_array();
|
||||||
|
}
|
||||||
|
if ($version != getID3::VERSION) {
|
||||||
|
$this->clear_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public: clear cache
|
||||||
|
public function clear_cache() {
|
||||||
|
$this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`');
|
||||||
|
$this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public: analyze file
|
||||||
|
public function analyze($filename, $filesize=null, $original_filename='') {
|
||||||
|
|
||||||
|
if (file_exists($filename)) {
|
||||||
|
|
||||||
|
// Short-hands
|
||||||
|
$filetime = filemtime($filename);
|
||||||
|
$filesize = filesize($filename);
|
||||||
|
|
||||||
|
// Lookup file
|
||||||
|
$SQLquery = 'SELECT `value`';
|
||||||
|
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
|
||||||
|
$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string($filename).'\')';
|
||||||
|
$SQLquery .= ' AND (`filesize` = \''.$this->mysqli->real_escape_string($filesize).'\')';
|
||||||
|
$SQLquery .= ' AND (`filetime` = \''.$this->mysqli->real_escape_string($filetime).'\')';
|
||||||
|
$this->cursor = $this->mysqli->query($SQLquery);
|
||||||
|
if ($this->cursor->num_rows > 0) {
|
||||||
|
// Hit
|
||||||
|
list($result) = $this->cursor->fetch_array();
|
||||||
|
return unserialize(base64_decode($result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Miss
|
||||||
|
$analysis = parent::analyze($filename, $filesize, $original_filename);
|
||||||
|
|
||||||
|
// Save result
|
||||||
|
if (file_exists($filename)) {
|
||||||
|
$SQLquery = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
|
||||||
|
$SQLquery .= '\''.$this->mysqli->real_escape_string($filename).'\'';
|
||||||
|
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filesize).'\'';
|
||||||
|
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filetime).'\'';
|
||||||
|
$SQLquery .= ', \''.$this->mysqli->real_escape_string(time() ).'\'';
|
||||||
|
$SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\')';
|
||||||
|
$this->cursor = $this->mysqli->query($SQLquery);
|
||||||
|
}
|
||||||
|
return $analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private: (re)create mysqli table
|
||||||
|
private function create_table($drop=false) {
|
||||||
|
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` (';
|
||||||
|
$SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
|
||||||
|
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
|
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
|
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
|
||||||
|
$SQLquery .= ', `value` LONGTEXT NOT NULL';
|
||||||
|
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci';
|
||||||
|
$this->cursor = $this->mysqli->query($SQLquery);
|
||||||
|
echo $this->mysqli->error;
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,185 +89,178 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class getID3_cached_sqlite3 extends getID3
|
class getID3_cached_sqlite3 extends getID3 {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __construct()
|
* __construct()
|
||||||
* @param string $table holds name of sqlite table
|
* @param string $table holds name of sqlite table
|
||||||
* @return type
|
* @return type
|
||||||
*/
|
*/
|
||||||
public function __construct($table = 'getid3_cache', $hide = false)
|
public function __construct($table='getid3_cache', $hide=false) {
|
||||||
{
|
$this->table = $table; // Set table
|
||||||
$this->table = $table; // Set table
|
$file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite';
|
||||||
$file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite';
|
if ($hide) {
|
||||||
if ($hide) {
|
$file = dirname(__FILE__).'/.ht.'.basename(__FILE__, 'php').'sqlite';
|
||||||
$file = dirname(__FILE__).'/.ht.'.basename(__FILE__, 'php').'sqlite';
|
}
|
||||||
}
|
$this->db = new SQLite3($file);
|
||||||
$this->db = new SQLite3($file);
|
$db = $this->db;
|
||||||
$db = $this->db;
|
$this->create_table(); // Create cache table if not exists
|
||||||
$this->create_table(); // Create cache table if not exists
|
$version = '';
|
||||||
$version = '';
|
$sql = $this->version_check;
|
||||||
$sql = $this->version_check;
|
$stmt = $db->prepare($sql);
|
||||||
$stmt = $db->prepare($sql);
|
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
|
$result = $stmt->execute();
|
||||||
$result = $stmt->execute();
|
list($version) = $result->fetchArray();
|
||||||
list($version) = $result->fetchArray();
|
if ($version != getID3::VERSION) { // Check version number and clear cache if changed
|
||||||
if ($version != getID3::VERSION) { // Check version number and clear cache if changed
|
$this->clear_cache();
|
||||||
$this->clear_cache();
|
}
|
||||||
}
|
return parent::__construct();
|
||||||
return parent::__construct();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* close the database connection
|
* close the database connection
|
||||||
*/
|
*/
|
||||||
public function __destruct()
|
public function __destruct() {
|
||||||
{
|
$db=$this->db;
|
||||||
$db=$this->db;
|
$db->close();
|
||||||
$db->close();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hold the sqlite db
|
* hold the sqlite db
|
||||||
* @var SQLite Resource
|
* @var SQLite Resource
|
||||||
*/
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* table to use for caching
|
* table to use for caching
|
||||||
* @var string $table
|
* @var string $table
|
||||||
*/
|
*/
|
||||||
private $table;
|
private $table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clear the cache
|
* clear the cache
|
||||||
* @access private
|
* @access private
|
||||||
* @return type
|
* @return type
|
||||||
*/
|
*/
|
||||||
private function clear_cache()
|
private function clear_cache() {
|
||||||
{
|
$db = $this->db;
|
||||||
$db = $this->db;
|
$sql = $this->delete_cache;
|
||||||
$sql = $this->delete_cache;
|
$db->exec($sql);
|
||||||
$db->exec($sql);
|
$sql = $this->set_version;
|
||||||
$sql = $this->set_version;
|
$stmt = $db->prepare($sql);
|
||||||
$stmt = $db->prepare($sql);
|
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
|
$stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT);
|
$stmt->bindValue(':val', getID3::VERSION, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':val', getID3::VERSION, SQLITE3_TEXT);
|
return $stmt->execute();
|
||||||
return $stmt->execute();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* analyze file and cache them, if cached pull from the db
|
* analyze file and cache them, if cached pull from the db
|
||||||
* @param type $filename
|
* @param type $filename
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function analyze($filename, $filesize = null, $original_filename = '')
|
public function analyze($filename, $filesize=null, $original_filename='') {
|
||||||
{
|
if (!file_exists($filename)) {
|
||||||
if (!file_exists($filename)) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
// items to track for caching
|
||||||
// items to track for caching
|
$filetime = filemtime($filename);
|
||||||
$filetime = filemtime($filename);
|
$filesize_real = filesize($filename);
|
||||||
$filesize_real = filesize($filename);
|
// this will be saved for a quick directory lookup of analized files
|
||||||
// this will be saved for a quick directory lookup of analized files
|
// ... why do 50 seperate sql quries when you can do 1 for the same result
|
||||||
// ... why do 50 seperate sql quries when you can do 1 for the same result
|
$dirname = dirname($filename);
|
||||||
$dirname = dirname($filename);
|
// Lookup file
|
||||||
// Lookup file
|
$db = $this->db;
|
||||||
$db = $this->db;
|
$sql = $this->get_id3_data;
|
||||||
$sql = $this->get_id3_data;
|
$stmt = $db->prepare($sql);
|
||||||
$stmt = $db->prepare($sql);
|
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
$stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER);
|
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
|
$res = $stmt->execute();
|
||||||
$res = $stmt->execute();
|
list($result) = $res->fetchArray();
|
||||||
list($result) = $res->fetchArray();
|
if (count($result) > 0 ) {
|
||||||
if (count($result) > 0) {
|
return unserialize(base64_decode($result));
|
||||||
return unserialize(base64_decode($result));
|
}
|
||||||
}
|
// if it hasn't been analyzed before, then do it now
|
||||||
// if it hasn't been analyzed before, then do it now
|
$analysis = parent::analyze($filename, $filesize, $original_filename);
|
||||||
$analysis = parent::analyze($filename, $filesize, $original_filename);
|
// Save result
|
||||||
// Save result
|
$sql = $this->cache_file;
|
||||||
$sql = $this->cache_file;
|
$stmt = $db->prepare($sql);
|
||||||
$stmt = $db->prepare($sql);
|
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
|
$stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT);
|
$stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER);
|
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
|
$stmt->bindValue(':atime', time(), SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':atime', time(), SQLITE3_INTEGER);
|
$stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT);
|
$res = $stmt->execute();
|
||||||
$res = $stmt->execute();
|
return $analysis;
|
||||||
return $analysis;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create data base table
|
* create data base table
|
||||||
* this is almost the same as MySQL, with the exception of the dirname being added
|
* this is almost the same as MySQL, with the exception of the dirname being added
|
||||||
* @return type
|
* @return type
|
||||||
*/
|
*/
|
||||||
private function create_table()
|
private function create_table() {
|
||||||
{
|
$db = $this->db;
|
||||||
$db = $this->db;
|
$sql = $this->make_table;
|
||||||
$sql = $this->make_table;
|
return $db->exec($sql);
|
||||||
return $db->exec($sql);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get cached directory
|
* get cached directory
|
||||||
*
|
*
|
||||||
* This function is not in the MySQL extention, it's ment to speed up requesting multiple files
|
* This function is not in the MySQL extention, it's ment to speed up requesting multiple files
|
||||||
* which is ideal for podcasting, playlists, etc.
|
* which is ideal for podcasting, playlists, etc.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $dir directory to search the cache database for
|
* @param string $dir directory to search the cache database for
|
||||||
* @return array return an array of matching id3 data
|
* @return array return an array of matching id3 data
|
||||||
*/
|
*/
|
||||||
public function get_cached_dir($dir)
|
public function get_cached_dir($dir) {
|
||||||
{
|
$db = $this->db;
|
||||||
$db = $this->db;
|
$rows = array();
|
||||||
$rows = [];
|
$sql = $this->get_cached_dir;
|
||||||
$sql = $this->get_cached_dir;
|
$stmt = $db->prepare($sql);
|
||||||
$stmt = $db->prepare($sql);
|
$stmt->bindValue(':dirname', $dir, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':dirname', $dir, SQLITE3_TEXT);
|
$res = $stmt->execute();
|
||||||
$res = $stmt->execute();
|
while ($row=$res->fetchArray()) {
|
||||||
while ($row=$res->fetchArray()) {
|
$rows[] = unserialize(base64_decode($row));
|
||||||
$rows[] = unserialize(base64_decode($row));
|
}
|
||||||
}
|
return $rows;
|
||||||
return $rows;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* use the magical __get() for sql queries
|
||||||
|
*
|
||||||
|
* access as easy as $this->{case name}, returns NULL if query is not found
|
||||||
|
*/
|
||||||
|
public function __get($name) {
|
||||||
|
switch($name) {
|
||||||
|
case 'version_check':
|
||||||
|
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'";
|
||||||
|
break;
|
||||||
|
case 'delete_cache':
|
||||||
|
return "DELETE FROM $this->table";
|
||||||
|
break;
|
||||||
|
case 'set_version':
|
||||||
|
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)";
|
||||||
|
break;
|
||||||
|
case 'get_id3_data':
|
||||||
|
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime";
|
||||||
|
break;
|
||||||
|
case 'cache_file':
|
||||||
|
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
|
||||||
|
break;
|
||||||
|
case 'make_table':
|
||||||
|
//return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
|
||||||
|
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))";
|
||||||
|
break;
|
||||||
|
case 'get_cached_dir':
|
||||||
|
return "SELECT val FROM $this->table WHERE dirname = :dirname";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* use the magical __get() for sql queries
|
|
||||||
*
|
|
||||||
* access as easy as $this->{case name}, returns NULL if query is not found
|
|
||||||
*/
|
|
||||||
public function __get($name)
|
|
||||||
{
|
|
||||||
switch ($name) {
|
|
||||||
case 'version_check':
|
|
||||||
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'";
|
|
||||||
break;
|
|
||||||
case 'delete_cache':
|
|
||||||
return "DELETE FROM $this->table";
|
|
||||||
break;
|
|
||||||
case 'set_version':
|
|
||||||
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)";
|
|
||||||
break;
|
|
||||||
case 'get_id3_data':
|
|
||||||
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime";
|
|
||||||
break;
|
|
||||||
case 'cache_file':
|
|
||||||
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
|
|
||||||
break;
|
|
||||||
case 'make_table':
|
|
||||||
//return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
|
|
||||||
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))";
|
|
||||||
break;
|
|
||||||
case 'get_cached_dir':
|
|
||||||
return "SELECT val FROM $this->table WHERE dirname = :dirname";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -20,265 +20,262 @@
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
class getid3_gzip extends getid3_handler
|
class getid3_gzip extends getid3_handler {
|
||||||
{
|
|
||||||
|
|
||||||
// public: Optional file list - disable for speed.
|
// public: Optional file list - disable for speed.
|
||||||
public $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
|
public $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'gzip';
|
$info['fileformat'] = 'gzip';
|
||||||
|
|
||||||
$start_length = 10;
|
$start_length = 10;
|
||||||
$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os';
|
$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os';
|
||||||
//+---+---+---+---+---+---+---+---+---+---+
|
//+---+---+---+---+---+---+---+---+---+---+
|
||||||
//|ID1|ID2|CM |FLG| MTIME |XFL|OS |
|
//|ID1|ID2|CM |FLG| MTIME |XFL|OS |
|
||||||
//+---+---+---+---+---+---+---+---+---+---+
|
//+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
|
||||||
if ($info['php_memory_limit'] && ($info['filesize'] > $info['php_memory_limit'])) {
|
if ($info['php_memory_limit'] && ($info['filesize'] > $info['php_memory_limit'])) {
|
||||||
$info['error'][] = 'File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)';
|
$this->error('File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$this->fseek(0);
|
$this->fseek(0);
|
||||||
$buffer = $this->fread($info['filesize']);
|
$buffer = $this->fread($info['filesize']);
|
||||||
|
|
||||||
$arr_members = explode("\x1F\x8B\x08", $buffer);
|
$arr_members = explode("\x1F\x8B\x08", $buffer);
|
||||||
while (true) {
|
while (true) {
|
||||||
$is_wrong_members = false;
|
$is_wrong_members = false;
|
||||||
$num_members = intval(count($arr_members));
|
$num_members = intval(count($arr_members));
|
||||||
for ($i = 0; $i < $num_members; $i++) {
|
for ($i = 0; $i < $num_members; $i++) {
|
||||||
if (strlen($arr_members[$i]) == 0) {
|
if (strlen($arr_members[$i]) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$buf = "\x1F\x8B\x08".$arr_members[$i];
|
$buf = "\x1F\x8B\x08".$arr_members[$i];
|
||||||
|
|
||||||
$attr = unpack($unpack_header, substr($buf, 0, $start_length));
|
$attr = unpack($unpack_header, substr($buf, 0, $start_length));
|
||||||
if (!$this->get_os_type(ord($attr['os']))) {
|
if (!$this->get_os_type(ord($attr['os']))) {
|
||||||
// Merge member with previous if wrong OS type
|
// Merge member with previous if wrong OS type
|
||||||
$arr_members[($i - 1)] .= $buf;
|
$arr_members[($i - 1)] .= $buf;
|
||||||
$arr_members[$i] = '';
|
$arr_members[$i] = '';
|
||||||
$is_wrong_members = true;
|
$is_wrong_members = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$is_wrong_members) {
|
if (!$is_wrong_members) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['gzip']['files'] = [];
|
$info['gzip']['files'] = array();
|
||||||
|
|
||||||
$fpointer = 0;
|
$fpointer = 0;
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
for ($i = 0; $i < $num_members; $i++) {
|
for ($i = 0; $i < $num_members; $i++) {
|
||||||
if (strlen($arr_members[$i]) == 0) {
|
if (strlen($arr_members[$i]) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$thisInfo = &$info['gzip']['member_header'][++$idx];
|
$thisInfo = &$info['gzip']['member_header'][++$idx];
|
||||||
|
|
||||||
$buff = "\x1F\x8B\x08".$arr_members[$i];
|
$buff = "\x1F\x8B\x08".$arr_members[$i];
|
||||||
|
|
||||||
$attr = unpack($unpack_header, substr($buff, 0, $start_length));
|
$attr = unpack($unpack_header, substr($buff, 0, $start_length));
|
||||||
$thisInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']);
|
$thisInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']);
|
||||||
$thisInfo['raw']['id1'] = ord($attr['cmethod']);
|
$thisInfo['raw']['id1'] = ord($attr['cmethod']);
|
||||||
$thisInfo['raw']['id2'] = ord($attr['cmethod']);
|
$thisInfo['raw']['id2'] = ord($attr['cmethod']);
|
||||||
$thisInfo['raw']['cmethod'] = ord($attr['cmethod']);
|
$thisInfo['raw']['cmethod'] = ord($attr['cmethod']);
|
||||||
$thisInfo['raw']['os'] = ord($attr['os']);
|
$thisInfo['raw']['os'] = ord($attr['os']);
|
||||||
$thisInfo['raw']['xflags'] = ord($attr['xflags']);
|
$thisInfo['raw']['xflags'] = ord($attr['xflags']);
|
||||||
$thisInfo['raw']['flags'] = ord($attr['flags']);
|
$thisInfo['raw']['flags'] = ord($attr['flags']);
|
||||||
|
|
||||||
$thisInfo['flags']['crc16'] = (bool) ($thisInfo['raw']['flags'] & 0x02);
|
$thisInfo['flags']['crc16'] = (bool) ($thisInfo['raw']['flags'] & 0x02);
|
||||||
$thisInfo['flags']['extra'] = (bool) ($thisInfo['raw']['flags'] & 0x04);
|
$thisInfo['flags']['extra'] = (bool) ($thisInfo['raw']['flags'] & 0x04);
|
||||||
$thisInfo['flags']['filename'] = (bool) ($thisInfo['raw']['flags'] & 0x08);
|
$thisInfo['flags']['filename'] = (bool) ($thisInfo['raw']['flags'] & 0x08);
|
||||||
$thisInfo['flags']['comment'] = (bool) ($thisInfo['raw']['flags'] & 0x10);
|
$thisInfo['flags']['comment'] = (bool) ($thisInfo['raw']['flags'] & 0x10);
|
||||||
|
|
||||||
$thisInfo['compression'] = $this->get_xflag_type($thisInfo['raw']['xflags']);
|
$thisInfo['compression'] = $this->get_xflag_type($thisInfo['raw']['xflags']);
|
||||||
|
|
||||||
$thisInfo['os'] = $this->get_os_type($thisInfo['raw']['os']);
|
$thisInfo['os'] = $this->get_os_type($thisInfo['raw']['os']);
|
||||||
if (!$thisInfo['os']) {
|
if (!$thisInfo['os']) {
|
||||||
$info['error'][] = 'Read error on gzip file';
|
$this->error('Read error on gzip file');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fpointer = 10;
|
$fpointer = 10;
|
||||||
$arr_xsubfield = [];
|
$arr_xsubfield = array();
|
||||||
// bit 2 - FLG.FEXTRA
|
// bit 2 - FLG.FEXTRA
|
||||||
//+---+---+=================================+
|
//+---+---+=================================+
|
||||||
//| XLEN |...XLEN bytes of "extra field"...|
|
//| XLEN |...XLEN bytes of "extra field"...|
|
||||||
//+---+---+=================================+
|
//+---+---+=================================+
|
||||||
if ($thisInfo['flags']['extra']) {
|
if ($thisInfo['flags']['extra']) {
|
||||||
$w_xlen = substr($buff, $fpointer, 2);
|
$w_xlen = substr($buff, $fpointer, 2);
|
||||||
$xlen = getid3_lib::LittleEndian2Int($w_xlen);
|
$xlen = getid3_lib::LittleEndian2Int($w_xlen);
|
||||||
$fpointer += 2;
|
$fpointer += 2;
|
||||||
|
|
||||||
$thisInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
|
$thisInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
|
||||||
// Extra SubFields
|
// Extra SubFields
|
||||||
//+---+---+---+---+==================================+
|
//+---+---+---+---+==================================+
|
||||||
//|SI1|SI2| LEN |... LEN bytes of subfield data ...|
|
//|SI1|SI2| LEN |... LEN bytes of subfield data ...|
|
||||||
//+---+---+---+---+==================================+
|
//+---+---+---+---+==================================+
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if ($idx >= $xlen) {
|
if ($idx >= $xlen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$si1 = ord(substr($buff, $fpointer + $idx++, 1));
|
$si1 = ord(substr($buff, $fpointer + $idx++, 1));
|
||||||
$si2 = ord(substr($buff, $fpointer + $idx++, 1));
|
$si2 = ord(substr($buff, $fpointer + $idx++, 1));
|
||||||
if (($si1 == 0x41) && ($si2 == 0x70)) {
|
if (($si1 == 0x41) && ($si2 == 0x70)) {
|
||||||
$w_xsublen = substr($buff, $fpointer + $idx, 2);
|
$w_xsublen = substr($buff, $fpointer + $idx, 2);
|
||||||
$xsublen = getid3_lib::LittleEndian2Int($w_xsublen);
|
$xsublen = getid3_lib::LittleEndian2Int($w_xsublen);
|
||||||
$idx += 2;
|
$idx += 2;
|
||||||
$arr_xsubfield[] = substr($buff, $fpointer + $idx, $xsublen);
|
$arr_xsubfield[] = substr($buff, $fpointer + $idx, $xsublen);
|
||||||
$idx += $xsublen;
|
$idx += $xsublen;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$fpointer += $xlen;
|
$fpointer += $xlen;
|
||||||
}
|
}
|
||||||
// bit 3 - FLG.FNAME
|
// bit 3 - FLG.FNAME
|
||||||
//+=========================================+
|
//+=========================================+
|
||||||
//|...original file name, zero-terminated...|
|
//|...original file name, zero-terminated...|
|
||||||
//+=========================================+
|
//+=========================================+
|
||||||
// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
|
// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
|
||||||
$thisInfo['filename'] = preg_replace('#\\.gz$#i', '', $info['filename']);
|
$thisInfo['filename'] = preg_replace('#\\.gz$#i', '', $info['filename']);
|
||||||
if ($thisInfo['flags']['filename']) {
|
if ($thisInfo['flags']['filename']) {
|
||||||
$thisInfo['filename'] = '';
|
$thisInfo['filename'] = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
if (ord($buff[$fpointer]) == 0) {
|
if (ord($buff[$fpointer]) == 0) {
|
||||||
$fpointer++;
|
$fpointer++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$thisInfo['filename'] .= $buff[$fpointer];
|
$thisInfo['filename'] .= $buff[$fpointer];
|
||||||
$fpointer++;
|
$fpointer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// bit 4 - FLG.FCOMMENT
|
// bit 4 - FLG.FCOMMENT
|
||||||
//+===================================+
|
//+===================================+
|
||||||
//|...file comment, zero-terminated...|
|
//|...file comment, zero-terminated...|
|
||||||
//+===================================+
|
//+===================================+
|
||||||
if ($thisInfo['flags']['comment']) {
|
if ($thisInfo['flags']['comment']) {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (ord($buff[$fpointer]) == 0) {
|
if (ord($buff[$fpointer]) == 0) {
|
||||||
$fpointer++;
|
$fpointer++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$thisInfo['comment'] .= $buff[$fpointer];
|
$thisInfo['comment'] .= $buff[$fpointer];
|
||||||
$fpointer++;
|
$fpointer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// bit 1 - FLG.FHCRC
|
// bit 1 - FLG.FHCRC
|
||||||
//+---+---+
|
//+---+---+
|
||||||
//| CRC16 |
|
//| CRC16 |
|
||||||
//+---+---+
|
//+---+---+
|
||||||
if ($thisInfo['flags']['crc16']) {
|
if ($thisInfo['flags']['crc16']) {
|
||||||
$w_crc = substr($buff, $fpointer, 2);
|
$w_crc = substr($buff, $fpointer, 2);
|
||||||
$thisInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
|
$thisInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
|
||||||
$fpointer += 2;
|
$fpointer += 2;
|
||||||
}
|
}
|
||||||
// bit 0 - FLG.FTEXT
|
// bit 0 - FLG.FTEXT
|
||||||
//if ($thisInfo['raw']['flags'] & 0x01) {
|
//if ($thisInfo['raw']['flags'] & 0x01) {
|
||||||
// Ignored...
|
// Ignored...
|
||||||
//}
|
//}
|
||||||
// bits 5, 6, 7 - reserved
|
// bits 5, 6, 7 - reserved
|
||||||
|
|
||||||
$thisInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
|
$thisInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
|
||||||
$thisInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
|
$thisInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
|
||||||
|
|
||||||
$info['gzip']['files'] = getid3_lib::array_merge_clobber($info['gzip']['files'], getid3_lib::CreateDeepArray($thisInfo['filename'], '/', $thisInfo['filesize']));
|
$info['gzip']['files'] = getid3_lib::array_merge_clobber($info['gzip']['files'], getid3_lib::CreateDeepArray($thisInfo['filename'], '/', $thisInfo['filesize']));
|
||||||
|
|
||||||
if ($this->option_gzip_parse_contents) {
|
if ($this->option_gzip_parse_contents) {
|
||||||
// Try to inflate GZip
|
// Try to inflate GZip
|
||||||
$csize = 0;
|
$csize = 0;
|
||||||
$inflated = '';
|
$inflated = '';
|
||||||
$chkcrc32 = '';
|
$chkcrc32 = '';
|
||||||
if (function_exists('gzinflate')) {
|
if (function_exists('gzinflate')) {
|
||||||
$cdata = substr($buff, $fpointer);
|
$cdata = substr($buff, $fpointer);
|
||||||
$cdata = substr($cdata, 0, strlen($cdata) - 8);
|
$cdata = substr($cdata, 0, strlen($cdata) - 8);
|
||||||
$csize = strlen($cdata);
|
$csize = strlen($cdata);
|
||||||
$inflated = gzinflate($cdata);
|
$inflated = gzinflate($cdata);
|
||||||
|
|
||||||
// Calculate CRC32 for inflated content
|
// Calculate CRC32 for inflated content
|
||||||
$thisInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisInfo['crc32']);
|
$thisInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisInfo['crc32']);
|
||||||
|
|
||||||
// determine format
|
// determine format
|
||||||
$formattest = substr($inflated, 0, 32774);
|
$formattest = substr($inflated, 0, 32774);
|
||||||
$getid3_temp = new getID3();
|
$getid3_temp = new getID3();
|
||||||
$determined_format = $getid3_temp->GetFileFormat($formattest);
|
$determined_format = $getid3_temp->GetFileFormat($formattest);
|
||||||
unset($getid3_temp);
|
unset($getid3_temp);
|
||||||
|
|
||||||
// file format is determined
|
// file format is determined
|
||||||
$determined_format['module'] = (isset($determined_format['module']) ? $determined_format['module'] : '');
|
$determined_format['module'] = (isset($determined_format['module']) ? $determined_format['module'] : '');
|
||||||
switch ($determined_format['module']) {
|
switch ($determined_format['module']) {
|
||||||
case 'tar':
|
case 'tar':
|
||||||
// view TAR-file info
|
// view TAR-file info
|
||||||
if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && include_once(GETID3_INCLUDEPATH.$determined_format['include'])) {
|
if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && include_once(GETID3_INCLUDEPATH.$determined_format['include'])) {
|
||||||
if (($temp_tar_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) {
|
if (($temp_tar_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) {
|
||||||
// can't find anywhere to create a temp file, abort
|
// can't find anywhere to create a temp file, abort
|
||||||
$info['error'][] = 'Unable to create temp file to parse TAR inside GZIP file';
|
$this->error('Unable to create temp file to parse TAR inside GZIP file');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($fp_temp_tar = fopen($temp_tar_filename, 'w+b')) {
|
if ($fp_temp_tar = fopen($temp_tar_filename, 'w+b')) {
|
||||||
fwrite($fp_temp_tar, $inflated);
|
fwrite($fp_temp_tar, $inflated);
|
||||||
fclose($fp_temp_tar);
|
fclose($fp_temp_tar);
|
||||||
$getid3_temp = new getID3();
|
$getid3_temp = new getID3();
|
||||||
$getid3_temp->openfile($temp_tar_filename);
|
$getid3_temp->openfile($temp_tar_filename);
|
||||||
$getid3_tar = new getid3_tar($getid3_temp);
|
$getid3_tar = new getid3_tar($getid3_temp);
|
||||||
$getid3_tar->Analyze();
|
$getid3_tar->Analyze();
|
||||||
$info['gzip']['member_header'][$idx]['tar'] = $getid3_temp->info['tar'];
|
$info['gzip']['member_header'][$idx]['tar'] = $getid3_temp->info['tar'];
|
||||||
unset($getid3_temp, $getid3_tar);
|
unset($getid3_temp, $getid3_tar);
|
||||||
unlink($temp_tar_filename);
|
unlink($temp_tar_filename);
|
||||||
} else {
|
} else {
|
||||||
$info['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file';
|
$this->error('Unable to fopen() temp file to parse TAR inside GZIP file');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '':
|
case '':
|
||||||
default:
|
default:
|
||||||
// unknown or unhandled format
|
// unknown or unhandled format
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the OS type
|
// Converts the OS type
|
||||||
public function get_os_type($key)
|
public function get_os_type($key) {
|
||||||
{
|
static $os_type = array(
|
||||||
static $os_type = [
|
'0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
|
||||||
'0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
|
'1' => 'Amiga',
|
||||||
'1' => 'Amiga',
|
'2' => 'VMS (or OpenVMS)',
|
||||||
'2' => 'VMS (or OpenVMS)',
|
'3' => 'Unix',
|
||||||
'3' => 'Unix',
|
'4' => 'VM/CMS',
|
||||||
'4' => 'VM/CMS',
|
'5' => 'Atari TOS',
|
||||||
'5' => 'Atari TOS',
|
'6' => 'HPFS filesystem (OS/2, NT)',
|
||||||
'6' => 'HPFS filesystem (OS/2, NT)',
|
'7' => 'Macintosh',
|
||||||
'7' => 'Macintosh',
|
'8' => 'Z-System',
|
||||||
'8' => 'Z-System',
|
'9' => 'CP/M',
|
||||||
'9' => 'CP/M',
|
'10' => 'TOPS-20',
|
||||||
'10' => 'TOPS-20',
|
'11' => 'NTFS filesystem (NT)',
|
||||||
'11' => 'NTFS filesystem (NT)',
|
'12' => 'QDOS',
|
||||||
'12' => 'QDOS',
|
'13' => 'Acorn RISCOS',
|
||||||
'13' => 'Acorn RISCOS',
|
'255' => 'unknown'
|
||||||
'255' => 'unknown'
|
);
|
||||||
];
|
return (isset($os_type[$key]) ? $os_type[$key] : '');
|
||||||
return (isset($os_type[$key]) ? $os_type[$key] : '');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Converts the eXtra FLags
|
// Converts the eXtra FLags
|
||||||
public function get_xflag_type($key)
|
public function get_xflag_type($key) {
|
||||||
{
|
static $xflag_type = array(
|
||||||
static $xflag_type = [
|
'0' => 'unknown',
|
||||||
'0' => 'unknown',
|
'2' => 'maximum compression',
|
||||||
'2' => 'maximum compression',
|
'4' => 'fastest algorithm'
|
||||||
'4' => 'fastest algorithm'
|
);
|
||||||
];
|
return (isset($xflag_type[$key]) ? $xflag_type[$key] : '');
|
||||||
return (isset($xflag_type[$key]) ? $xflag_type[$key] : '');
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,33 +18,34 @@
|
||||||
class getid3_rar extends getid3_handler
|
class getid3_rar extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public $option_use_rar_extension = false;
|
public $option_use_rar_extension = false;
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'rar';
|
$info['fileformat'] = 'rar';
|
||||||
|
|
||||||
|
if ($this->option_use_rar_extension === true) {
|
||||||
|
if (function_exists('rar_open')) {
|
||||||
|
if ($rp = rar_open($info['filenamepath'])) {
|
||||||
|
$info['rar']['files'] = array();
|
||||||
|
$entries = rar_list($rp);
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$info['rar']['files'] = getid3_lib::array_merge_clobber($info['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
|
||||||
|
}
|
||||||
|
rar_close($rp);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$this->error('failed to rar_open('.$info['filename'].')');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->error('RAR support does not appear to be available in this PHP installation');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->error('PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->option_use_rar_extension === true) {
|
|
||||||
if (function_exists('rar_open')) {
|
|
||||||
if ($rp = rar_open($info['filenamepath'])) {
|
|
||||||
$info['rar']['files'] = [];
|
|
||||||
$entries = rar_list($rp);
|
|
||||||
foreach ($entries as $entry) {
|
|
||||||
$info['rar']['files'] = getid3_lib::array_merge_clobber($info['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
|
|
||||||
}
|
|
||||||
rar_close($rp);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$info['error'][] = 'failed to rar_open('.$info['filename'].')';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$info['error'][] = 'RAR support does not appear to be available in this PHP installation';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$info['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,79 +18,80 @@
|
||||||
class getid3_szip extends getid3_handler
|
class getid3_szip extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$SZIPHeader = $this->fread(6);
|
$SZIPHeader = $this->fread(6);
|
||||||
if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") {
|
if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") {
|
||||||
$info['error'][] = 'Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"';
|
$this->error('Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'szip';
|
$info['fileformat'] = 'szip';
|
||||||
$info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
|
$info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
|
||||||
$info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
|
$info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
|
||||||
$info['error'][] = 'SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (!$this->feof()) {
|
while (!$this->feof()) {
|
||||||
$NextBlockID = $this->fread(2);
|
$NextBlockID = $this->fread(2);
|
||||||
switch ($NextBlockID) {
|
switch ($NextBlockID) {
|
||||||
case 'SZ':
|
case 'SZ':
|
||||||
// Note that szip files can be concatenated, this has the same effect as
|
// Note that szip files can be concatenated, this has the same effect as
|
||||||
// concatenating the files. this also means that global header blocks
|
// concatenating the files. this also means that global header blocks
|
||||||
// might be present between directory/data blocks.
|
// might be present between directory/data blocks.
|
||||||
$this->fseek(4, SEEK_CUR);
|
$this->fseek(4, SEEK_CUR);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'BH':
|
case 'BH':
|
||||||
$BHheaderbytes = getid3_lib::BigEndian2Int($this->fread(3));
|
$BHheaderbytes = getid3_lib::BigEndian2Int($this->fread(3));
|
||||||
$BHheaderdata = $this->fread($BHheaderbytes);
|
$BHheaderdata = $this->fread($BHheaderbytes);
|
||||||
$BHheaderoffset = 0;
|
$BHheaderoffset = 0;
|
||||||
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
|
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
|
||||||
//filename as \0 terminated string (empty string indicates end)
|
//filename as \0 terminated string (empty string indicates end)
|
||||||
//owner as \0 terminated string (empty is same as last file)
|
//owner as \0 terminated string (empty is same as last file)
|
||||||
//group 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
|
//3 byte filelength in this block
|
||||||
//2 byte access flags
|
//2 byte access flags
|
||||||
//4 byte creation time (like in unix)
|
//4 byte creation time (like in unix)
|
||||||
//4 byte modification time (like in unix)
|
//4 byte modification time (like in unix)
|
||||||
//4 byte access time (like in unix)
|
//4 byte access time (like in unix)
|
||||||
|
|
||||||
$BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
$BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||||
$BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
|
$BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
|
||||||
|
|
||||||
$BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
$BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||||
$BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
|
$BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
|
||||||
|
|
||||||
$BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
$BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
|
||||||
$BHheaderoffset += (strlen($BHdataArray['group']) + 1);
|
$BHheaderoffset += (strlen($BHdataArray['group']) + 1);
|
||||||
|
|
||||||
$BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
|
$BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
|
||||||
$BHheaderoffset += 3;
|
$BHheaderoffset += 3;
|
||||||
|
|
||||||
$BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
|
$BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
|
||||||
$BHheaderoffset += 2;
|
$BHheaderoffset += 2;
|
||||||
|
|
||||||
$BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
$BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||||
$BHheaderoffset += 4;
|
$BHheaderoffset += 4;
|
||||||
|
|
||||||
$BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
$BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||||
$BHheaderoffset += 4;
|
$BHheaderoffset += 4;
|
||||||
|
|
||||||
$BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
$BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
|
||||||
$BHheaderoffset += 4;
|
$BHheaderoffset += 4;
|
||||||
|
|
||||||
$info['szip']['BH'][] = $BHdataArray;
|
$info['szip']['BH'][] = $BHdataArray;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break 2;
|
break 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,172 +23,155 @@
|
||||||
class getid3_tar extends getid3_handler
|
class getid3_tar extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'tar';
|
$info['fileformat'] = 'tar';
|
||||||
$info['tar']['files'] = [];
|
$info['tar']['files'] = array();
|
||||||
|
|
||||||
$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix';
|
$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
|
$null_512k = str_repeat("\x00", 512); // end-of-file marker
|
||||||
|
|
||||||
$this->fseek(0);
|
$this->fseek(0);
|
||||||
while (!feof($this->getid3->fp)) {
|
while (!feof($this->getid3->fp)) {
|
||||||
$buffer = $this->fread(512);
|
$buffer = $this->fread(512);
|
||||||
if (strlen($buffer) < 512) {
|
if (strlen($buffer) < 512) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the block
|
// check the block
|
||||||
$checksum = 0;
|
$checksum = 0;
|
||||||
for ($i = 0; $i < 148; $i++) {
|
for ($i = 0; $i < 148; $i++) {
|
||||||
$checksum += ord($buffer{$i});
|
$checksum += ord($buffer{$i});
|
||||||
}
|
}
|
||||||
for ($i = 148; $i < 156; $i++) {
|
for ($i = 148; $i < 156; $i++) {
|
||||||
$checksum += ord(' ');
|
$checksum += ord(' ');
|
||||||
}
|
}
|
||||||
for ($i = 156; $i < 512; $i++) {
|
for ($i = 156; $i < 512; $i++) {
|
||||||
$checksum += ord($buffer{$i});
|
$checksum += ord($buffer{$i});
|
||||||
}
|
}
|
||||||
$attr = unpack($unpack_header, $buffer);
|
$attr = unpack($unpack_header, $buffer);
|
||||||
$name = (isset($attr['fname']) ? trim($attr['fname']) : '');
|
$name = (isset($attr['fname'] ) ? trim($attr['fname'] ) : '');
|
||||||
$mode = octdec(isset($attr['mode']) ? trim($attr['mode']) : '');
|
$mode = octdec(isset($attr['mode'] ) ? trim($attr['mode'] ) : '');
|
||||||
$uid = octdec(isset($attr['uid']) ? trim($attr['uid']) : '');
|
$uid = octdec(isset($attr['uid'] ) ? trim($attr['uid'] ) : '');
|
||||||
$gid = octdec(isset($attr['gid']) ? trim($attr['gid']) : '');
|
$gid = octdec(isset($attr['gid'] ) ? trim($attr['gid'] ) : '');
|
||||||
$size = octdec(isset($attr['size']) ? trim($attr['size']) : '');
|
$size = octdec(isset($attr['size'] ) ? trim($attr['size'] ) : '');
|
||||||
$mtime = octdec(isset($attr['mtime']) ? trim($attr['mtime']) : '');
|
$mtime = octdec(isset($attr['mtime'] ) ? trim($attr['mtime'] ) : '');
|
||||||
$chksum = octdec(isset($attr['chksum']) ? trim($attr['chksum']) : '');
|
$chksum = octdec(isset($attr['chksum'] ) ? trim($attr['chksum'] ) : '');
|
||||||
$typflag = (isset($attr['typflag']) ? trim($attr['typflag']) : '');
|
$typflag = (isset($attr['typflag']) ? trim($attr['typflag']) : '');
|
||||||
$lnkname = (isset($attr['lnkname']) ? trim($attr['lnkname']) : '');
|
$lnkname = (isset($attr['lnkname']) ? trim($attr['lnkname']) : '');
|
||||||
$magic = (isset($attr['magic']) ? trim($attr['magic']) : '');
|
$magic = (isset($attr['magic'] ) ? trim($attr['magic'] ) : '');
|
||||||
$ver = (isset($attr['ver']) ? trim($attr['ver']) : '');
|
$ver = (isset($attr['ver'] ) ? trim($attr['ver'] ) : '');
|
||||||
$uname = (isset($attr['uname']) ? trim($attr['uname']) : '');
|
$uname = (isset($attr['uname'] ) ? trim($attr['uname'] ) : '');
|
||||||
$gname = (isset($attr['gname']) ? trim($attr['gname']) : '');
|
$gname = (isset($attr['gname'] ) ? trim($attr['gname'] ) : '');
|
||||||
$devmaj = octdec(isset($attr['devmaj']) ? trim($attr['devmaj']) : '');
|
$devmaj = octdec(isset($attr['devmaj'] ) ? trim($attr['devmaj'] ) : '');
|
||||||
$devmin = octdec(isset($attr['devmin']) ? trim($attr['devmin']) : '');
|
$devmin = octdec(isset($attr['devmin'] ) ? trim($attr['devmin'] ) : '');
|
||||||
$prefix = (isset($attr['prefix']) ? trim($attr['prefix']) : '');
|
$prefix = (isset($attr['prefix'] ) ? trim($attr['prefix'] ) : '');
|
||||||
if (($checksum == 256) && ($chksum == 0)) {
|
if (($checksum == 256) && ($chksum == 0)) {
|
||||||
// EOF Found
|
// EOF Found
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($prefix) {
|
if ($prefix) {
|
||||||
$name = $prefix.'/'.$name;
|
$name = $prefix.'/'.$name;
|
||||||
}
|
}
|
||||||
if ((preg_match('#/$#', $name)) && !$name) {
|
if ((preg_match('#/$#', $name)) && !$name) {
|
||||||
$typeflag = 5;
|
$typeflag = 5;
|
||||||
}
|
}
|
||||||
if ($buffer == $null_512k) {
|
if ($buffer == $null_512k) {
|
||||||
// it's the end of the tar-file...
|
// it's the end of the tar-file...
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read to the next chunk
|
// Read to the next chunk
|
||||||
$this->fseek($size, SEEK_CUR);
|
$this->fseek($size, SEEK_CUR);
|
||||||
|
|
||||||
$diff = $size % 512;
|
$diff = $size % 512;
|
||||||
if ($diff != 0) {
|
if ($diff != 0) {
|
||||||
// Padding, throw away
|
// Padding, throw away
|
||||||
$this->fseek((512 - $diff), SEEK_CUR);
|
$this->fseek((512 - $diff), SEEK_CUR);
|
||||||
}
|
}
|
||||||
// Protect against tar-files with garbage at the end
|
// Protect against tar-files with garbage at the end
|
||||||
if ($name == '') {
|
if ($name == '') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$info['tar']['file_details'][$name] = [
|
$info['tar']['file_details'][$name] = array (
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'mode_raw' => $mode,
|
'mode_raw' => $mode,
|
||||||
'mode' => self::display_perms($mode),
|
'mode' => self::display_perms($mode),
|
||||||
'uid' => $uid,
|
'uid' => $uid,
|
||||||
'gid' => $gid,
|
'gid' => $gid,
|
||||||
'size' => $size,
|
'size' => $size,
|
||||||
'mtime' => $mtime,
|
'mtime' => $mtime,
|
||||||
'chksum' => $chksum,
|
'chksum' => $chksum,
|
||||||
'typeflag' => self::get_flag_type($typflag),
|
'typeflag' => self::get_flag_type($typflag),
|
||||||
'linkname' => $lnkname,
|
'linkname' => $lnkname,
|
||||||
'magic' => $magic,
|
'magic' => $magic,
|
||||||
'version' => $ver,
|
'version' => $ver,
|
||||||
'uname' => $uname,
|
'uname' => $uname,
|
||||||
'gname' => $gname,
|
'gname' => $gname,
|
||||||
'devmajor' => $devmaj,
|
'devmajor' => $devmaj,
|
||||||
'devminor' => $devmin
|
'devminor' => $devmin
|
||||||
];
|
);
|
||||||
$info['tar']['files'] = getid3_lib::array_merge_clobber($info['tar']['files'], getid3_lib::CreateDeepArray($info['tar']['file_details'][$name]['name'], '/', $size));
|
$info['tar']['files'] = getid3_lib::array_merge_clobber($info['tar']['files'], getid3_lib::CreateDeepArray($info['tar']['file_details'][$name]['name'], '/', $size));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the file mode to file permissions
|
// Parses the file mode to file permissions
|
||||||
public function display_perms($mode)
|
public function display_perms($mode) {
|
||||||
{
|
// Determine Type
|
||||||
// Determine Type
|
if ($mode & 0x1000) $type='p'; // FIFO pipe
|
||||||
if ($mode & 0x1000) {
|
elseif ($mode & 0x2000) $type='c'; // Character special
|
||||||
$type='p'; // FIFO pipe
|
elseif ($mode & 0x4000) $type='d'; // Directory
|
||||||
} elseif ($mode & 0x2000) {
|
elseif ($mode & 0x6000) $type='b'; // Block special
|
||||||
$type='c'; // Character special
|
elseif ($mode & 0x8000) $type='-'; // Regular
|
||||||
} elseif ($mode & 0x4000) {
|
elseif ($mode & 0xA000) $type='l'; // Symbolic Link
|
||||||
$type='d'; // Directory
|
elseif ($mode & 0xC000) $type='s'; // Socket
|
||||||
} elseif ($mode & 0x6000) {
|
else $type='u'; // UNKNOWN
|
||||||
$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
|
// Determine permissions
|
||||||
$owner['read'] = (($mode & 00400) ? 'r' : '-');
|
$owner['read'] = (($mode & 00400) ? 'r' : '-');
|
||||||
$owner['write'] = (($mode & 00200) ? 'w' : '-');
|
$owner['write'] = (($mode & 00200) ? 'w' : '-');
|
||||||
$owner['execute'] = (($mode & 00100) ? 'x' : '-');
|
$owner['execute'] = (($mode & 00100) ? 'x' : '-');
|
||||||
$group['read'] = (($mode & 00040) ? 'r' : '-');
|
$group['read'] = (($mode & 00040) ? 'r' : '-');
|
||||||
$group['write'] = (($mode & 00020) ? 'w' : '-');
|
$group['write'] = (($mode & 00020) ? 'w' : '-');
|
||||||
$group['execute'] = (($mode & 00010) ? 'x' : '-');
|
$group['execute'] = (($mode & 00010) ? 'x' : '-');
|
||||||
$world['read'] = (($mode & 00004) ? 'r' : '-');
|
$world['read'] = (($mode & 00004) ? 'r' : '-');
|
||||||
$world['write'] = (($mode & 00002) ? 'w' : '-');
|
$world['write'] = (($mode & 00002) ? 'w' : '-');
|
||||||
$world['execute'] = (($mode & 00001) ? 'x' : '-');
|
$world['execute'] = (($mode & 00001) ? 'x' : '-');
|
||||||
|
|
||||||
// Adjust for SUID, SGID and sticky bit
|
// Adjust for SUID, SGID and sticky bit
|
||||||
if ($mode & 0x800) {
|
if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
|
||||||
$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';
|
||||||
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', $type);
|
||||||
$s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']);
|
$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', $group['read'], $group['write'], $group['execute']);
|
||||||
$s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']);
|
$s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']);
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts the file type
|
||||||
|
public 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 (isset($flag_types[$typflag]) ? $flag_types[$typflag] : '');
|
||||||
|
}
|
||||||
|
|
||||||
// Converts the file type
|
|
||||||
public function get_flag_type($typflag)
|
|
||||||
{
|
|
||||||
static $flag_types = [
|
|
||||||
'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 (isset($flag_types[$typflag]) ? $flag_types[$typflag] : '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,502 +18,496 @@
|
||||||
class getid3_zip extends getid3_handler
|
class getid3_zip extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
$info['fileformat'] = 'zip';
|
||||||
$info['fileformat'] = 'zip';
|
$info['zip']['encoding'] = 'ISO-8859-1';
|
||||||
$info['zip']['encoding'] = 'ISO-8859-1';
|
$info['zip']['files'] = array();
|
||||||
$info['zip']['files'] = [];
|
|
||||||
|
$info['zip']['compressed_size'] = 0;
|
||||||
$info['zip']['compressed_size'] = 0;
|
$info['zip']['uncompressed_size'] = 0;
|
||||||
$info['zip']['uncompressed_size'] = 0;
|
$info['zip']['entries_count'] = 0;
|
||||||
$info['zip']['entries_count'] = 0;
|
|
||||||
|
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
||||||
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
$this->error('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB, not supported by PHP');
|
||||||
$info['error'][] = 'File is larger than '.round(PHP_INT_MAX / 1073741824).'GB, not supported by PHP';
|
return false;
|
||||||
return false;
|
} else {
|
||||||
} else {
|
$EOCDsearchData = '';
|
||||||
$EOCDsearchData = '';
|
$EOCDsearchCounter = 0;
|
||||||
$EOCDsearchCounter = 0;
|
while ($EOCDsearchCounter++ < 512) {
|
||||||
while ($EOCDsearchCounter++ < 512) {
|
|
||||||
$this->fseek(-128 * $EOCDsearchCounter, SEEK_END);
|
$this->fseek(-128 * $EOCDsearchCounter, SEEK_END);
|
||||||
$EOCDsearchData = $this->fread(128).$EOCDsearchData;
|
$EOCDsearchData = $this->fread(128).$EOCDsearchData;
|
||||||
|
|
||||||
if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
|
if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
|
||||||
$EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
|
|
||||||
$this->fseek((-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
|
$EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
|
||||||
$info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory();
|
$this->fseek((-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
|
||||||
|
$info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory();
|
||||||
$this->fseek($info['zip']['end_central_directory']['directory_offset']);
|
|
||||||
$info['zip']['entries_count'] = 0;
|
$this->fseek($info['zip']['end_central_directory']['directory_offset']);
|
||||||
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
|
$info['zip']['entries_count'] = 0;
|
||||||
$info['zip']['central_directory'][] = $centraldirectoryentry;
|
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
|
||||||
$info['zip']['entries_count']++;
|
$info['zip']['central_directory'][] = $centraldirectoryentry;
|
||||||
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
$info['zip']['entries_count']++;
|
||||||
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
||||||
|
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
||||||
//if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid
|
|
||||||
if (!empty($centraldirectoryentry['filename'])) {
|
//if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid
|
||||||
$info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
|
if (!empty($centraldirectoryentry['filename'])) {
|
||||||
}
|
$info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ($info['zip']['entries_count'] == 0) {
|
|
||||||
$info['error'][] = 'No Central Directory entries found (truncated file?)';
|
if ($info['zip']['entries_count'] == 0) {
|
||||||
return false;
|
$this->error('No Central Directory entries found (truncated file?)');
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
if (!empty($info['zip']['end_central_directory']['comment'])) {
|
|
||||||
$info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
|
if (!empty($info['zip']['end_central_directory']['comment'])) {
|
||||||
}
|
$info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
|
||||||
|
}
|
||||||
if (isset($info['zip']['central_directory'][0]['compression_method'])) {
|
|
||||||
$info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method'];
|
if (isset($info['zip']['central_directory'][0]['compression_method'])) {
|
||||||
}
|
$info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method'];
|
||||||
if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) {
|
}
|
||||||
$info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed'];
|
if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) {
|
||||||
}
|
$info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed'];
|
||||||
if (isset($info['zip']['compression_method']) && ($info['zip']['compression_method'] == 'store') && !isset($info['zip']['compression_speed'])) {
|
}
|
||||||
$info['zip']['compression_speed'] = 'store';
|
if (isset($info['zip']['compression_method']) && ($info['zip']['compression_method'] == 'store') && !isset($info['zip']['compression_speed'])) {
|
||||||
}
|
$info['zip']['compression_speed'] = 'store';
|
||||||
|
}
|
||||||
// secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each
|
|
||||||
// Local File Header entry will
|
// secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each
|
||||||
foreach ($info['zip']['central_directory'] as $central_directory_entry) {
|
// Local File Header entry will
|
||||||
$this->fseek($central_directory_entry['entry_offset']);
|
foreach ($info['zip']['central_directory'] as $central_directory_entry) {
|
||||||
if ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
$this->fseek($central_directory_entry['entry_offset']);
|
||||||
$info['zip']['entries'][] = $fileentry;
|
if ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
||||||
} else {
|
$info['zip']['entries'][] = $fileentry;
|
||||||
$info['warning'][] = 'Error parsing Local File Header at offset '.$central_directory_entry['entry_offset'];
|
} else {
|
||||||
}
|
$this->warning('Error parsing Local File Header at offset '.$central_directory_entry['entry_offset']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!empty($info['zip']['files']['[Content_Types].xml']) &&
|
|
||||||
!empty($info['zip']['files']['_rels']['.rels']) &&
|
if (!empty($info['zip']['files']['[Content_Types].xml']) &&
|
||||||
!empty($info['zip']['files']['docProps']['app.xml']) &&
|
!empty($info['zip']['files']['_rels']['.rels']) &&
|
||||||
!empty($info['zip']['files']['docProps']['core.xml'])) {
|
!empty($info['zip']['files']['docProps']['app.xml']) &&
|
||||||
// http://technet.microsoft.com/en-us/library/cc179224.aspx
|
!empty($info['zip']['files']['docProps']['core.xml'])) {
|
||||||
$info['fileformat'] = 'zip.msoffice';
|
// http://technet.microsoft.com/en-us/library/cc179224.aspx
|
||||||
if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
|
$info['fileformat'] = 'zip.msoffice';
|
||||||
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
|
||||||
} elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
|
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
||||||
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
} elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
|
||||||
} elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
|
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||||
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
} elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
|
||||||
}
|
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!$this->getZIPentriesFilepointer()) {
|
|
||||||
unset($info['zip']);
|
if (!$this->getZIPentriesFilepointer()) {
|
||||||
$info['fileformat'] = '';
|
unset($info['zip']);
|
||||||
$info['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
|
$info['fileformat'] = '';
|
||||||
return false;
|
$this->error('Cannot find End Of Central Directory (truncated file?)');
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
// 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'] > ($info['filesize'] - 46 - 22)) {
|
// scan through actual file data entries, recover as much as possible from probable trucated file
|
||||||
$info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)';
|
if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) {
|
||||||
}
|
$this->error('Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)');
|
||||||
$info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
|
}
|
||||||
foreach ($info['zip']['entries'] as $key => $valuearray) {
|
$this->error('Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete');
|
||||||
$info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
|
foreach ($info['zip']['entries'] as $key => $valuearray) {
|
||||||
}
|
$info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
|
||||||
return true;
|
}
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function getZIPHeaderFilepointerTopDown()
|
|
||||||
{
|
public function getZIPHeaderFilepointerTopDown() {
|
||||||
$info = &$this->getid3->info;
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
$info['fileformat'] = 'zip';
|
$info['fileformat'] = 'zip';
|
||||||
|
|
||||||
$info['zip']['compressed_size'] = 0;
|
$info['zip']['compressed_size'] = 0;
|
||||||
$info['zip']['uncompressed_size'] = 0;
|
$info['zip']['uncompressed_size'] = 0;
|
||||||
$info['zip']['entries_count'] = 0;
|
$info['zip']['entries_count'] = 0;
|
||||||
|
|
||||||
rewind($this->getid3->fp);
|
rewind($this->getid3->fp);
|
||||||
while ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
while ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
||||||
$info['zip']['entries'][] = $fileentry;
|
$info['zip']['entries'][] = $fileentry;
|
||||||
$info['zip']['entries_count']++;
|
$info['zip']['entries_count']++;
|
||||||
}
|
}
|
||||||
if ($info['zip']['entries_count'] == 0) {
|
if ($info['zip']['entries_count'] == 0) {
|
||||||
$info['error'][] = 'No Local File Header entries found';
|
$this->error('No Local File Header entries found');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['zip']['entries_count'] = 0;
|
$info['zip']['entries_count'] = 0;
|
||||||
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
|
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
|
||||||
$info['zip']['central_directory'][] = $centraldirectoryentry;
|
$info['zip']['central_directory'][] = $centraldirectoryentry;
|
||||||
$info['zip']['entries_count']++;
|
$info['zip']['entries_count']++;
|
||||||
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
|
||||||
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
|
||||||
}
|
}
|
||||||
if ($info['zip']['entries_count'] == 0) {
|
if ($info['zip']['entries_count'] == 0) {
|
||||||
$info['error'][] = 'No Central Directory entries found (truncated file?)';
|
$this->error('No Central Directory entries found (truncated file?)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($EOCD = $this->ZIPparseEndOfCentralDirectory()) {
|
if ($EOCD = $this->ZIPparseEndOfCentralDirectory()) {
|
||||||
$info['zip']['end_central_directory'] = $EOCD;
|
$info['zip']['end_central_directory'] = $EOCD;
|
||||||
} else {
|
} else {
|
||||||
$info['error'][] = 'No End Of Central Directory entry found (truncated file?)';
|
$this->error('No End Of Central Directory entry found (truncated file?)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($info['zip']['end_central_directory']['comment'])) {
|
if (!empty($info['zip']['end_central_directory']['comment'])) {
|
||||||
$info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
|
$info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getZIPentriesFilepointer()
|
public function getZIPentriesFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
$info['zip']['compressed_size'] = 0;
|
||||||
$info['zip']['compressed_size'] = 0;
|
$info['zip']['uncompressed_size'] = 0;
|
||||||
$info['zip']['uncompressed_size'] = 0;
|
$info['zip']['entries_count'] = 0;
|
||||||
$info['zip']['entries_count'] = 0;
|
|
||||||
|
rewind($this->getid3->fp);
|
||||||
rewind($this->getid3->fp);
|
while ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
||||||
while ($fileentry = $this->ZIPparseLocalFileHeader()) {
|
$info['zip']['entries'][] = $fileentry;
|
||||||
$info['zip']['entries'][] = $fileentry;
|
$info['zip']['entries_count']++;
|
||||||
$info['zip']['entries_count']++;
|
$info['zip']['compressed_size'] += $fileentry['compressed_size'];
|
||||||
$info['zip']['compressed_size'] += $fileentry['compressed_size'];
|
$info['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
|
||||||
$info['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
|
}
|
||||||
}
|
if ($info['zip']['entries_count'] == 0) {
|
||||||
if ($info['zip']['entries_count'] == 0) {
|
$this->error('No Local File Header entries found');
|
||||||
$info['error'][] = 'No Local File Header entries found';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public function ZIPparseLocalFileHeader() {
|
||||||
public function ZIPparseLocalFileHeader()
|
$LocalFileHeader['offset'] = $this->ftell();
|
||||||
{
|
|
||||||
$LocalFileHeader['offset'] = $this->ftell();
|
$ZIPlocalFileHeader = $this->fread(30);
|
||||||
|
|
||||||
$ZIPlocalFileHeader = $this->fread(30);
|
$LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
|
||||||
|
if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // "PK\x03\x04"
|
||||||
$LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
|
// invalid Local File Header Signature
|
||||||
if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // "PK\x03\x04"
|
$this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
|
||||||
// invalid Local File Header Signature
|
return false;
|
||||||
$this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
|
}
|
||||||
return false;
|
$LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
|
||||||
}
|
$LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2));
|
||||||
$LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
|
$LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2));
|
||||||
$LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2));
|
$LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
|
||||||
$LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2));
|
$LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
|
||||||
$LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
|
$LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
|
||||||
$LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
|
$LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
|
||||||
$LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
|
$LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
|
||||||
$LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
|
$LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
|
||||||
$LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
|
$LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
|
||||||
$LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
|
|
||||||
$LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
|
$LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10);
|
||||||
|
$LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8);
|
||||||
$LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10);
|
$LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']);
|
||||||
$LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8);
|
$LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size'];
|
||||||
$LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']);
|
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size'];
|
||||||
$LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size'];
|
$LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
|
||||||
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size'];
|
$LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
|
||||||
$LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
|
|
||||||
$LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
|
$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
|
||||||
|
if ($FilenameExtrafieldLength > 0) {
|
||||||
$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
|
$ZIPlocalFileHeader .= $this->fread($FilenameExtrafieldLength);
|
||||||
if ($FilenameExtrafieldLength > 0) {
|
|
||||||
$ZIPlocalFileHeader .= $this->fread($FilenameExtrafieldLength);
|
if ($LocalFileHeader['raw']['filename_length'] > 0) {
|
||||||
|
$LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
|
||||||
if ($LocalFileHeader['raw']['filename_length'] > 0) {
|
}
|
||||||
$LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
|
if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
|
||||||
}
|
$LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
|
||||||
if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
|
}
|
||||||
$LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
|
}
|
||||||
}
|
|
||||||
}
|
if ($LocalFileHeader['compressed_size'] == 0) {
|
||||||
|
// *Could* be a zero-byte file
|
||||||
if ($LocalFileHeader['compressed_size'] == 0) {
|
// But could also be a file written on the fly that didn't know compressed filesize beforehand.
|
||||||
// *Could* be a zero-byte file
|
// Correct compressed filesize should be in the data_descriptor located after this file data, and also in Central Directory (at end of zip file)
|
||||||
// But could also be a file written on the fly that didn't know compressed filesize beforehand.
|
if (!empty($this->getid3->info['zip']['central_directory'])) {
|
||||||
// Correct compressed filesize should be in the data_descriptor located after this file data, and also in Central Directory (at end of zip file)
|
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
|
||||||
if (!empty($this->getid3->info['zip']['central_directory'])) {
|
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
|
||||||
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
|
if ($central_directory_entry['compressed_size'] > 0) {
|
||||||
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
|
// overwrite local zero value (but not ['raw']'compressed_size']) so that seeking for data_descriptor (and next file entry) works correctly
|
||||||
if ($central_directory_entry['compressed_size'] > 0) {
|
$LocalFileHeader['compressed_size'] = $central_directory_entry['compressed_size'];
|
||||||
// overwrite local zero value (but not ['raw']'compressed_size']) so that seeking for data_descriptor (and next file entry) works correctly
|
}
|
||||||
$LocalFileHeader['compressed_size'] = $central_directory_entry['compressed_size'];
|
break;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
$LocalFileHeader['data_offset'] = $this->ftell();
|
||||||
$LocalFileHeader['data_offset'] = $this->ftell();
|
$this->fseek($LocalFileHeader['compressed_size'], SEEK_CUR); // this should (but may not) match value in $LocalFileHeader['raw']['compressed_size'] -- $LocalFileHeader['compressed_size'] could have been overwritten above with value from Central Directory
|
||||||
$this->fseek($LocalFileHeader['compressed_size'], SEEK_CUR); // this should (but may not) match value in $LocalFileHeader['raw']['compressed_size'] -- $LocalFileHeader['compressed_size'] could have been overwritten above with value from Central Directory
|
|
||||||
|
if ($LocalFileHeader['flags']['data_descriptor_used']) {
|
||||||
if ($LocalFileHeader['flags']['data_descriptor_used']) {
|
$DataDescriptor = $this->fread(16);
|
||||||
$DataDescriptor = $this->fread(16);
|
$LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
|
||||||
$LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
|
if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08"
|
||||||
if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08"
|
$this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']);
|
||||||
$this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']);
|
$this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
|
||||||
$this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
|
||||||
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
|
$LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
|
||||||
$LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
|
$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 12, 4));
|
||||||
$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 12, 4));
|
if (!$LocalFileHeader['raw']['compressed_size'] && $LocalFileHeader['data_descriptor']['compressed_size']) {
|
||||||
if (!$LocalFileHeader['raw']['compressed_size'] && $LocalFileHeader['data_descriptor']['compressed_size']) {
|
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
|
||||||
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
|
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
|
||||||
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
|
if ($LocalFileHeader['data_descriptor']['compressed_size'] == $central_directory_entry['compressed_size']) {
|
||||||
if ($LocalFileHeader['data_descriptor']['compressed_size'] == $central_directory_entry['compressed_size']) {
|
// $LocalFileHeader['compressed_size'] already set from Central Directory
|
||||||
// $LocalFileHeader['compressed_size'] already set from Central Directory
|
} else {
|
||||||
} else {
|
$this->warning('conflicting compressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['compressed_size'].') vs Central Directory ('.$central_directory_entry['compressed_size'].') for file at offset '.$LocalFileHeader['offset']);
|
||||||
$this->getid3->info['warning'][] = 'conflicting compressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['compressed_size'].') vs Central Directory ('.$central_directory_entry['compressed_size'].') for file at offset '.$LocalFileHeader['offset'];
|
}
|
||||||
}
|
|
||||||
|
if ($LocalFileHeader['data_descriptor']['uncompressed_size'] == $central_directory_entry['uncompressed_size']) {
|
||||||
if ($LocalFileHeader['data_descriptor']['uncompressed_size'] == $central_directory_entry['uncompressed_size']) {
|
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['data_descriptor']['uncompressed_size'];
|
||||||
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['data_descriptor']['uncompressed_size'];
|
} else {
|
||||||
} else {
|
$this->warning('conflicting uncompressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['uncompressed_size'].') vs Central Directory ('.$central_directory_entry['uncompressed_size'].') for file at offset '.$LocalFileHeader['offset']);
|
||||||
$this->getid3->info['warning'][] = 'conflicting uncompressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['uncompressed_size'].') vs Central Directory ('.$central_directory_entry['uncompressed_size'].') for file at offset '.$LocalFileHeader['offset'];
|
}
|
||||||
}
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return $LocalFileHeader;
|
||||||
return $LocalFileHeader;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public function ZIPparseCentralDirectory() {
|
||||||
public function ZIPparseCentralDirectory()
|
$CentralDirectory['offset'] = $this->ftell();
|
||||||
{
|
|
||||||
$CentralDirectory['offset'] = $this->ftell();
|
$ZIPcentralDirectory = $this->fread(46);
|
||||||
|
|
||||||
$ZIPcentralDirectory = $this->fread(46);
|
$CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4));
|
||||||
|
if ($CentralDirectory['raw']['signature'] != 0x02014B50) {
|
||||||
$CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4));
|
// invalid Central Directory Signature
|
||||||
if ($CentralDirectory['raw']['signature'] != 0x02014B50) {
|
$this->fseek($CentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
|
||||||
// invalid Central Directory Signature
|
return false;
|
||||||
$this->fseek($CentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
|
}
|
||||||
return false;
|
$CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
|
||||||
}
|
$CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2));
|
||||||
$CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
|
$CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2));
|
||||||
$CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2));
|
$CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
|
||||||
$CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2));
|
$CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
|
||||||
$CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
|
$CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
|
||||||
$CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
|
$CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
|
||||||
$CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
|
$CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
|
||||||
$CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
|
$CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
|
||||||
$CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
|
$CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
|
||||||
$CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
|
$CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
|
||||||
$CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
|
$CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
|
||||||
$CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
|
$CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2));
|
||||||
$CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
|
$CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2));
|
||||||
$CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2));
|
$CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4));
|
||||||
$CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2));
|
$CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
|
||||||
$CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4));
|
|
||||||
$CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
|
$CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset'];
|
||||||
|
$CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
|
||||||
$CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset'];
|
$CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
|
||||||
$CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
|
$CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
|
||||||
$CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
|
$CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
|
||||||
$CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
|
$CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size'];
|
||||||
$CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
|
$CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size'];
|
||||||
$CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size'];
|
$CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
|
||||||
$CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size'];
|
$CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
|
||||||
$CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
|
|
||||||
$CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
|
$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
|
||||||
|
if ($FilenameExtrafieldCommentLength > 0) {
|
||||||
$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
|
$FilenameExtrafieldComment = $this->fread($FilenameExtrafieldCommentLength);
|
||||||
if ($FilenameExtrafieldCommentLength > 0) {
|
|
||||||
$FilenameExtrafieldComment = $this->fread($FilenameExtrafieldCommentLength);
|
if ($CentralDirectory['raw']['filename_length'] > 0) {
|
||||||
|
$CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
|
||||||
if ($CentralDirectory['raw']['filename_length'] > 0) {
|
}
|
||||||
$CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
|
if ($CentralDirectory['raw']['extra_field_length'] > 0) {
|
||||||
}
|
$CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']);
|
||||||
if ($CentralDirectory['raw']['extra_field_length'] > 0) {
|
}
|
||||||
$CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']);
|
if ($CentralDirectory['raw']['file_comment_length'] > 0) {
|
||||||
}
|
$CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
|
||||||
if ($CentralDirectory['raw']['file_comment_length'] > 0) {
|
}
|
||||||
$CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
|
}
|
||||||
}
|
|
||||||
}
|
return $CentralDirectory;
|
||||||
|
}
|
||||||
return $CentralDirectory;
|
|
||||||
}
|
public function ZIPparseEndOfCentralDirectory() {
|
||||||
|
$EndOfCentralDirectory['offset'] = $this->ftell();
|
||||||
public function ZIPparseEndOfCentralDirectory()
|
|
||||||
{
|
$ZIPendOfCentralDirectory = $this->fread(22);
|
||||||
$EndOfCentralDirectory['offset'] = $this->ftell();
|
|
||||||
|
$EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
|
||||||
$ZIPendOfCentralDirectory = $this->fread(22);
|
if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
|
||||||
|
// invalid End Of Central Directory Signature
|
||||||
$EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
|
$this->fseek($EndOfCentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
|
||||||
if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
|
return false;
|
||||||
// invalid End Of Central Directory Signature
|
}
|
||||||
$this->fseek($EndOfCentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
|
$EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
|
||||||
return false;
|
$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['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
|
$EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
|
||||||
$EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2));
|
$EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
|
||||||
$EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2));
|
$EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
|
||||||
$EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
|
$EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
|
||||||
$EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
|
|
||||||
$EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
|
if ($EndOfCentralDirectory['comment_length'] > 0) {
|
||||||
$EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
|
$EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']);
|
||||||
|
}
|
||||||
if ($EndOfCentralDirectory['comment_length'] > 0) {
|
|
||||||
$EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']);
|
return $EndOfCentralDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $EndOfCentralDirectory;
|
|
||||||
}
|
public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
|
||||||
|
// https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html
|
||||||
|
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
|
||||||
public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod)
|
// 0x0002 -- see below
|
||||||
{
|
// 0x0004 -- see below
|
||||||
// https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html
|
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
|
||||||
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
|
$ParsedFlags['enhanced_deflation'] = (bool) ($flagbytes & 0x0010);
|
||||||
// 0x0002 -- see below
|
$ParsedFlags['compressed_patched_data'] = (bool) ($flagbytes & 0x0020);
|
||||||
// 0x0004 -- see below
|
$ParsedFlags['strong_encryption'] = (bool) ($flagbytes & 0x0040);
|
||||||
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
|
// 0x0080 - unused
|
||||||
$ParsedFlags['enhanced_deflation'] = (bool) ($flagbytes & 0x0010);
|
// 0x0100 - unused
|
||||||
$ParsedFlags['compressed_patched_data'] = (bool) ($flagbytes & 0x0020);
|
// 0x0200 - unused
|
||||||
$ParsedFlags['strong_encryption'] = (bool) ($flagbytes & 0x0040);
|
// 0x0400 - unused
|
||||||
// 0x0080 - unused
|
$ParsedFlags['language_encoding'] = (bool) ($flagbytes & 0x0800);
|
||||||
// 0x0100 - unused
|
// 0x1000 - reserved
|
||||||
// 0x0200 - unused
|
$ParsedFlags['mask_header_values'] = (bool) ($flagbytes & 0x2000);
|
||||||
// 0x0400 - unused
|
// 0x4000 - reserved
|
||||||
$ParsedFlags['language_encoding'] = (bool) ($flagbytes & 0x0800);
|
// 0x8000 - reserved
|
||||||
// 0x1000 - reserved
|
|
||||||
$ParsedFlags['mask_header_values'] = (bool) ($flagbytes & 0x2000);
|
switch ($compressionmethod) {
|
||||||
// 0x4000 - reserved
|
case 6:
|
||||||
// 0x8000 - reserved
|
$ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096);
|
||||||
|
$ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2);
|
||||||
switch ($compressionmethod) {
|
break;
|
||||||
case 6:
|
|
||||||
$ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096);
|
case 8:
|
||||||
$ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2);
|
case 9:
|
||||||
break;
|
switch (($flagbytes & 0x0006) >> 1) {
|
||||||
|
case 0:
|
||||||
case 8:
|
$ParsedFlags['compression_speed'] = 'normal';
|
||||||
case 9:
|
break;
|
||||||
switch (($flagbytes & 0x0006) >> 1) {
|
case 1:
|
||||||
case 0:
|
$ParsedFlags['compression_speed'] = 'maximum';
|
||||||
$ParsedFlags['compression_speed'] = 'normal';
|
break;
|
||||||
break;
|
case 2:
|
||||||
case 1:
|
$ParsedFlags['compression_speed'] = 'fast';
|
||||||
$ParsedFlags['compression_speed'] = 'maximum';
|
break;
|
||||||
break;
|
case 3:
|
||||||
case 2:
|
$ParsedFlags['compression_speed'] = 'superfast';
|
||||||
$ParsedFlags['compression_speed'] = 'fast';
|
break;
|
||||||
break;
|
}
|
||||||
case 3:
|
break;
|
||||||
$ParsedFlags['compression_speed'] = 'superfast';
|
}
|
||||||
break;
|
|
||||||
}
|
return $ParsedFlags;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $ParsedFlags;
|
public static function ZIPversionOSLookup($index) {
|
||||||
}
|
static $ZIPversionOSLookup = array(
|
||||||
|
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
|
||||||
|
1 => 'Amiga',
|
||||||
public static function ZIPversionOSLookup($index)
|
2 => 'OpenVMS',
|
||||||
{
|
3 => 'Unix',
|
||||||
static $ZIPversionOSLookup = [
|
4 => 'VM/CMS',
|
||||||
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
|
5 => 'Atari ST',
|
||||||
1 => 'Amiga',
|
6 => 'OS/2 H.P.F.S.',
|
||||||
2 => 'OpenVMS',
|
7 => 'Macintosh',
|
||||||
3 => 'Unix',
|
8 => 'Z-System',
|
||||||
4 => 'VM/CMS',
|
9 => 'CP/M',
|
||||||
5 => 'Atari ST',
|
10 => 'Windows NTFS',
|
||||||
6 => 'OS/2 H.P.F.S.',
|
11 => 'MVS',
|
||||||
7 => 'Macintosh',
|
12 => 'VSE',
|
||||||
8 => 'Z-System',
|
13 => 'Acorn Risc',
|
||||||
9 => 'CP/M',
|
14 => 'VFAT',
|
||||||
10 => 'Windows NTFS',
|
15 => 'Alternate MVS',
|
||||||
11 => 'MVS',
|
16 => 'BeOS',
|
||||||
12 => 'VSE',
|
17 => 'Tandem',
|
||||||
13 => 'Acorn Risc',
|
18 => 'OS/400',
|
||||||
14 => 'VFAT',
|
19 => 'OS/X (Darwin)',
|
||||||
15 => 'Alternate MVS',
|
);
|
||||||
16 => 'BeOS',
|
|
||||||
17 => 'Tandem',
|
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
|
||||||
18 => 'OS/400',
|
}
|
||||||
19 => 'OS/X (Darwin)',
|
|
||||||
];
|
public static function ZIPcompressionMethodLookup($index) {
|
||||||
|
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html
|
||||||
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
|
static $ZIPcompressionMethodLookup = array(
|
||||||
}
|
0 => 'store',
|
||||||
|
1 => 'shrink',
|
||||||
public static function ZIPcompressionMethodLookup($index)
|
2 => 'reduce-1',
|
||||||
{
|
3 => 'reduce-2',
|
||||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html
|
4 => 'reduce-3',
|
||||||
static $ZIPcompressionMethodLookup = [
|
5 => 'reduce-4',
|
||||||
0 => 'store',
|
6 => 'implode',
|
||||||
1 => 'shrink',
|
7 => 'tokenize',
|
||||||
2 => 'reduce-1',
|
8 => 'deflate',
|
||||||
3 => 'reduce-2',
|
9 => 'deflate64',
|
||||||
4 => 'reduce-3',
|
10 => 'Imploded (old IBM TERSE)',
|
||||||
5 => 'reduce-4',
|
11 => 'RESERVED[11]',
|
||||||
6 => 'implode',
|
12 => 'BZIP2',
|
||||||
7 => 'tokenize',
|
13 => 'RESERVED[13]',
|
||||||
8 => 'deflate',
|
14 => 'LZMA (EFS)',
|
||||||
9 => 'deflate64',
|
15 => 'RESERVED[15]',
|
||||||
10 => 'Imploded (old IBM TERSE)',
|
16 => 'RESERVED[16]',
|
||||||
11 => 'RESERVED[11]',
|
17 => 'RESERVED[17]',
|
||||||
12 => 'BZIP2',
|
18 => 'IBM TERSE (new)',
|
||||||
13 => 'RESERVED[13]',
|
19 => 'IBM LZ77 z Architecture (PFS)',
|
||||||
14 => 'LZMA (EFS)',
|
96 => 'JPEG recompressed',
|
||||||
15 => 'RESERVED[15]',
|
97 => 'WavPack compressed',
|
||||||
16 => 'RESERVED[16]',
|
98 => 'PPMd version I, Rev 1',
|
||||||
17 => 'RESERVED[17]',
|
);
|
||||||
18 => 'IBM TERSE (new)',
|
|
||||||
19 => 'IBM LZ77 z Architecture (PFS)',
|
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
|
||||||
96 => 'JPEG recompressed',
|
}
|
||||||
97 => 'WavPack compressed',
|
|
||||||
98 => 'PPMd version I, Rev 1',
|
public static function DOStime2UNIXtime($DOSdate, $DOStime) {
|
||||||
];
|
// wFatDate
|
||||||
|
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
|
||||||
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
|
// Bits Contents
|
||||||
}
|
// 0-4 Day of the month (1-31)
|
||||||
|
// 5-8 Month (1 = January, 2 = February, and so on)
|
||||||
public static function DOStime2UNIXtime($DOSdate, $DOStime)
|
// 9-15 Year offset from 1980 (add 1980 to get actual year)
|
||||||
{
|
|
||||||
// wFatDate
|
$UNIXday = ($DOSdate & 0x001F);
|
||||||
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
|
$UNIXmonth = (($DOSdate & 0x01E0) >> 5);
|
||||||
// Bits Contents
|
$UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980;
|
||||||
// 0-4 Day of the month (1-31)
|
|
||||||
// 5-8 Month (1 = January, 2 = February, and so on)
|
// wFatTime
|
||||||
// 9-15 Year offset from 1980 (add 1980 to get actual year)
|
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
|
||||||
|
// Bits Contents
|
||||||
$UNIXday = ($DOSdate & 0x001F);
|
// 0-4 Second divided by 2
|
||||||
$UNIXmonth = (($DOSdate & 0x01E0) >> 5);
|
// 5-10 Minute (0-59)
|
||||||
$UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980;
|
// 11-15 Hour (0-23 on a 24-hour clock)
|
||||||
|
|
||||||
// wFatTime
|
$UNIXsecond = ($DOStime & 0x001F) * 2;
|
||||||
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
|
$UNIXminute = (($DOStime & 0x07E0) >> 5);
|
||||||
// Bits Contents
|
$UNIXhour = (($DOStime & 0xF800) >> 11);
|
||||||
// 0-4 Second divided by 2
|
|
||||||
// 5-10 Minute (0-59)
|
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
||||||
// 11-15 Hour (0-23 on a 24-hour clock)
|
}
|
||||||
|
|
||||||
$UNIXsecond = ($DOStime & 0x001F) * 2;
|
|
||||||
$UNIXminute = (($DOStime & 0x07E0) >> 5);
|
|
||||||
$UNIXhour = (($DOStime & 0xF800) >> 11);
|
|
||||||
|
|
||||||
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,56 +18,55 @@
|
||||||
class getid3_bink extends getid3_handler
|
class getid3_bink extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['error'][] = 'Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']';
|
$this->error('Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']');
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$fileTypeID = $this->fread(3);
|
$fileTypeID = $this->fread(3);
|
||||||
switch ($fileTypeID) {
|
switch ($fileTypeID) {
|
||||||
case 'BIK':
|
case 'BIK':
|
||||||
return $this->ParseBink();
|
return $this->ParseBink();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'SMK':
|
case 'SMK':
|
||||||
return $this->ParseSmacker();
|
return $this->ParseSmacker();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$info['error'][] = 'Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"';
|
$this->error('Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"');
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
public function ParseBink()
|
}
|
||||||
{
|
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$info['fileformat'] = 'bink';
|
|
||||||
$info['video']['dataformat'] = 'bink';
|
|
||||||
|
|
||||||
$fileData = 'BIK'.$this->fread(13);
|
public function ParseBink() {
|
||||||
|
$info = &$this->getid3->info;
|
||||||
|
$info['fileformat'] = 'bink';
|
||||||
|
$info['video']['dataformat'] = 'bink';
|
||||||
|
|
||||||
$info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
$fileData = 'BIK'.$this->fread(13);
|
||||||
$info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
|
|
||||||
|
|
||||||
if (($info['avdataend'] - $info['avdataoffset']) != ($info['bink']['data_size'] + 8)) {
|
$info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
|
||||||
$info['error'][] = 'Probably truncated file: expecting '.$info['bink']['data_size'].' bytes, found '.($info['avdataend'] - $info['avdataoffset']);
|
$info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (($info['avdataend'] - $info['avdataoffset']) != ($info['bink']['data_size'] + 8)) {
|
||||||
}
|
$this->error('Probably truncated file: expecting '.$info['bink']['data_size'].' bytes, found '.($info['avdataend'] - $info['avdataoffset']));
|
||||||
|
}
|
||||||
|
|
||||||
public function ParseSmacker()
|
return true;
|
||||||
{
|
}
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$info['fileformat'] = 'smacker';
|
public function ParseSmacker() {
|
||||||
$info['video']['dataformat'] = 'smacker';
|
$info = &$this->getid3->info;
|
||||||
|
$info['fileformat'] = 'smacker';
|
||||||
|
$info['video']['dataformat'] = 'smacker';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -16,326 +16,325 @@
|
||||||
|
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
|
||||||
|
|
||||||
class getid3_mpeg extends getid3_handler
|
class getid3_mpeg extends getid3_handler {
|
||||||
{
|
|
||||||
|
|
||||||
const START_CODE_BASE = "\x00\x00\x01";
|
const START_CODE_BASE = "\x00\x00\x01";
|
||||||
const VIDEO_PICTURE_START = "\x00\x00\x01\x00";
|
const VIDEO_PICTURE_START = "\x00\x00\x01\x00";
|
||||||
const VIDEO_USER_DATA_START = "\x00\x00\x01\xB2";
|
const VIDEO_USER_DATA_START = "\x00\x00\x01\xB2";
|
||||||
const VIDEO_SEQUENCE_HEADER = "\x00\x00\x01\xB3";
|
const VIDEO_SEQUENCE_HEADER = "\x00\x00\x01\xB3";
|
||||||
const VIDEO_SEQUENCE_ERROR = "\x00\x00\x01\xB4";
|
const VIDEO_SEQUENCE_ERROR = "\x00\x00\x01\xB4";
|
||||||
const VIDEO_EXTENSION_START = "\x00\x00\x01\xB5";
|
const VIDEO_EXTENSION_START = "\x00\x00\x01\xB5";
|
||||||
const VIDEO_SEQUENCE_END = "\x00\x00\x01\xB7";
|
const VIDEO_SEQUENCE_END = "\x00\x00\x01\xB7";
|
||||||
const VIDEO_GROUP_START = "\x00\x00\x01\xB8";
|
const VIDEO_GROUP_START = "\x00\x00\x01\xB8";
|
||||||
const AUDIO_START = "\x00\x00\x01\xC0";
|
const AUDIO_START = "\x00\x00\x01\xC0";
|
||||||
|
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'mpeg';
|
$info['fileformat'] = 'mpeg';
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
$MPEGstreamData = $this->fread($this->getid3->option_fread_buffer_size);
|
$MPEGstreamData = $this->fread($this->getid3->option_fread_buffer_size);
|
||||||
$MPEGstreamBaseOffset = 0; // how far are we from the beginning of the file data ($info['avdataoffset'])
|
$MPEGstreamBaseOffset = 0; // how far are we from the beginning of the file data ($info['avdataoffset'])
|
||||||
$MPEGstreamDataOffset = 0; // how far are we from the beginning of the buffer data (~32kB)
|
$MPEGstreamDataOffset = 0; // how far are we from the beginning of the buffer data (~32kB)
|
||||||
|
|
||||||
$StartCodeValue = false;
|
$StartCodeValue = false;
|
||||||
$prevStartCodeValue = false;
|
$prevStartCodeValue = false;
|
||||||
|
|
||||||
$GOPcounter = -1;
|
$GOPcounter = -1;
|
||||||
$FramesByGOP = [];
|
$FramesByGOP = array();
|
||||||
$ParsedAVchannels = [];
|
$ParsedAVchannels = array();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
//echo $MPEGstreamDataOffset.' vs '.(strlen($MPEGstreamData) - 1024).'<Br>';
|
//echo $MPEGstreamDataOffset.' vs '.(strlen($MPEGstreamData) - 1024).'<Br>';
|
||||||
if ($MPEGstreamDataOffset > (strlen($MPEGstreamData) - 16384)) {
|
if ($MPEGstreamDataOffset > (strlen($MPEGstreamData) - 16384)) {
|
||||||
// buffer running low, get more data
|
// buffer running low, get more data
|
||||||
//echo 'reading more data<br>';
|
//echo 'reading more data<br>';
|
||||||
$MPEGstreamData .= $this->fread($this->getid3->option_fread_buffer_size);
|
$MPEGstreamData .= $this->fread($this->getid3->option_fread_buffer_size);
|
||||||
if (strlen($MPEGstreamData) > $this->getid3->option_fread_buffer_size) {
|
if (strlen($MPEGstreamData) > $this->getid3->option_fread_buffer_size) {
|
||||||
$MPEGstreamData = substr($MPEGstreamData, $MPEGstreamDataOffset);
|
$MPEGstreamData = substr($MPEGstreamData, $MPEGstreamDataOffset);
|
||||||
$MPEGstreamBaseOffset += $MPEGstreamDataOffset;
|
$MPEGstreamBaseOffset += $MPEGstreamDataOffset;
|
||||||
$MPEGstreamDataOffset = 0;
|
$MPEGstreamDataOffset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (($StartCodeOffset = strpos($MPEGstreamData, self::START_CODE_BASE, $MPEGstreamDataOffset)) === false) {
|
if (($StartCodeOffset = strpos($MPEGstreamData, self::START_CODE_BASE, $MPEGstreamDataOffset)) === false) {
|
||||||
//echo 'no more start codes found.<br>';
|
//echo 'no more start codes found.<br>';
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
$MPEGstreamDataOffset = $StartCodeOffset;
|
$MPEGstreamDataOffset = $StartCodeOffset;
|
||||||
$prevStartCodeValue = $StartCodeValue;
|
$prevStartCodeValue = $StartCodeValue;
|
||||||
$StartCodeValue = ord(substr($MPEGstreamData, $StartCodeOffset + 3, 1));
|
$StartCodeValue = ord(substr($MPEGstreamData, $StartCodeOffset + 3, 1));
|
||||||
//echo 'Found "'.strtoupper(dechex($StartCodeValue)).'" at offset '.($MPEGstreamBaseOffset + $StartCodeOffset).' ($MPEGstreamDataOffset = '.$MPEGstreamDataOffset.')<br>';
|
//echo 'Found "'.strtoupper(dechex($StartCodeValue)).'" at offset '.($MPEGstreamBaseOffset + $StartCodeOffset).' ($MPEGstreamDataOffset = '.$MPEGstreamDataOffset.')<br>';
|
||||||
}
|
}
|
||||||
$MPEGstreamDataOffset += 4;
|
$MPEGstreamDataOffset += 4;
|
||||||
switch ($StartCodeValue) {
|
switch ($StartCodeValue) {
|
||||||
case 0x00: // picture_start_code
|
|
||||||
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
|
|
||||||
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4));
|
|
||||||
$bitstreamoffset = 0;
|
|
||||||
|
|
||||||
$PictureHeader = [];
|
case 0x00: // picture_start_code
|
||||||
|
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
|
||||||
|
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4));
|
||||||
|
$bitstreamoffset = 0;
|
||||||
|
|
||||||
$PictureHeader['temporal_reference'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10-bit unsigned integer associated with each input picture. It is incremented by one, modulo 1024, for each input frame. When a frame is coded as two fields the temporal reference in the picture header of both fields is the same. Following a group start header the temporal reference of the earliest picture (in display order) shall be reset to zero.
|
$PictureHeader = array();
|
||||||
$PictureHeader['picture_coding_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_coding_type
|
|
||||||
$PictureHeader['vbv_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 16); // 16 bits for vbv_delay
|
|
||||||
//... etc
|
|
||||||
|
|
||||||
$FramesByGOP[$GOPcounter][] = $PictureHeader;
|
$PictureHeader['temporal_reference'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10-bit unsigned integer associated with each input picture. It is incremented by one, modulo 1024, for each input frame. When a frame is coded as two fields the temporal reference in the picture header of both fields is the same. Following a group start header the temporal reference of the earliest picture (in display order) shall be reset to zero.
|
||||||
}
|
$PictureHeader['picture_coding_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_coding_type
|
||||||
break;
|
$PictureHeader['vbv_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 16); // 16 bits for vbv_delay
|
||||||
|
//... etc
|
||||||
|
|
||||||
case 0xB3: // sequence_header_code
|
$FramesByGOP[$GOPcounter][] = $PictureHeader;
|
||||||
/*
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xB3: // sequence_header_code
|
||||||
|
/*
|
||||||
Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
|
Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
|
||||||
Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
|
Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
|
||||||
Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
|
Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
|
||||||
*/
|
*/
|
||||||
$info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found
|
$info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found
|
||||||
|
|
||||||
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
|
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
|
||||||
$bitstreamoffset = 0;
|
$bitstreamoffset = 0;
|
||||||
|
|
||||||
$info['mpeg']['video']['raw']['horizontal_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for horizontal frame size. Note: horizontal_size_extension, if present, will add 2 most-significant bits to this value
|
$info['mpeg']['video']['raw']['horizontal_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for horizontal frame size. Note: horizontal_size_extension, if present, will add 2 most-significant bits to this value
|
||||||
$info['mpeg']['video']['raw']['vertical_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for vertical frame size. Note: vertical_size_extension, if present, will add 2 most-significant bits to this value
|
$info['mpeg']['video']['raw']['vertical_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for vertical frame size. Note: vertical_size_extension, if present, will add 2 most-significant bits to this value
|
||||||
$info['mpeg']['video']['raw']['aspect_ratio_information'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for aspect_ratio_information
|
$info['mpeg']['video']['raw']['aspect_ratio_information'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for aspect_ratio_information
|
||||||
$info['mpeg']['video']['raw']['frame_rate_code'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for Frame Rate id code
|
$info['mpeg']['video']['raw']['frame_rate_code'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for Frame Rate id code
|
||||||
$info['mpeg']['video']['raw']['bitrate'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 18); // 18 bits for bit_rate_value (18 set bits = VBR, otherwise bitrate = this value * 400)
|
$info['mpeg']['video']['raw']['bitrate'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 18); // 18 bits for bit_rate_value (18 set bits = VBR, otherwise bitrate = this value * 400)
|
||||||
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
||||||
$info['mpeg']['video']['raw']['vbv_buffer_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10 bits vbv_buffer_size_value
|
$info['mpeg']['video']['raw']['vbv_buffer_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10 bits vbv_buffer_size_value
|
||||||
$info['mpeg']['video']['raw']['constrained_param_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: constrained_param_flag
|
$info['mpeg']['video']['raw']['constrained_param_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: constrained_param_flag
|
||||||
$info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: load_intra_quantiser_matrix
|
$info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: load_intra_quantiser_matrix
|
||||||
|
|
||||||
if ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix']) {
|
if ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix']) {
|
||||||
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12, 64));
|
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12, 64));
|
||||||
for ($i = 0; $i < 64; $i++) {
|
for ($i = 0; $i < 64; $i++) {
|
||||||
$info['mpeg']['video']['raw']['intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
|
$info['mpeg']['video']['raw']['intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
|
$info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
|
||||||
|
|
||||||
if ($info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix']) {
|
if ($info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix']) {
|
||||||
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12 + ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] ? 64 : 0), 64));
|
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12 + ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] ? 64 : 0), 64));
|
||||||
for ($i = 0; $i < 64; $i++) {
|
for ($i = 0; $i < 64; $i++) {
|
||||||
$info['mpeg']['video']['raw']['non_intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
|
$info['mpeg']['video']['raw']['non_intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
|
$info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
|
||||||
$info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
|
$info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
|
||||||
$info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']);
|
$info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']);
|
||||||
if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits = VBR
|
if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits = VBR
|
||||||
//$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files');
|
//$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files');
|
||||||
$info['mpeg']['video']['bitrate_mode'] = 'vbr';
|
$info['mpeg']['video']['bitrate_mode'] = 'vbr';
|
||||||
} else {
|
} else {
|
||||||
$info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
|
$info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
|
||||||
$info['mpeg']['video']['bitrate_mode'] = 'cbr';
|
$info['mpeg']['video']['bitrate_mode'] = 'cbr';
|
||||||
$info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
|
$info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
|
||||||
}
|
}
|
||||||
$info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_value'];
|
$info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_value'];
|
||||||
$info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_value'];
|
$info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_value'];
|
||||||
$info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
|
$info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
|
||||||
$info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
|
$info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
|
||||||
$info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
|
$info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
$info['video']['bits_per_sample'] = 24;
|
$info['video']['bits_per_sample'] = 24;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xB5: // extension_start_code
|
case 0xB5: // extension_start_code
|
||||||
$info['video']['codec'] = 'MPEG-2';
|
$info['video']['codec'] = 'MPEG-2';
|
||||||
|
|
||||||
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8)); // 48 bits for Sequence Extension ID; 61 bits for Sequence Display Extension ID; 59 bits for Sequence Scalable Extension ID
|
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8)); // 48 bits for Sequence Extension ID; 61 bits for Sequence Display Extension ID; 59 bits for Sequence Scalable Extension ID
|
||||||
$bitstreamoffset = 0;
|
$bitstreamoffset = 0;
|
||||||
|
|
||||||
$info['mpeg']['video']['raw']['extension_start_code_identifier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for extension_start_code_identifier
|
$info['mpeg']['video']['raw']['extension_start_code_identifier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for extension_start_code_identifier
|
||||||
//echo $info['mpeg']['video']['raw']['extension_start_code_identifier'].'<br>';
|
//echo $info['mpeg']['video']['raw']['extension_start_code_identifier'].'<br>';
|
||||||
switch ($info['mpeg']['video']['raw']['extension_start_code_identifier']) {
|
switch ($info['mpeg']['video']['raw']['extension_start_code_identifier']) {
|
||||||
case 1: // 0001 Sequence Extension ID
|
case 1: // 0001 Sequence Extension ID
|
||||||
$info['mpeg']['video']['raw']['profile_and_level_indication'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for profile_and_level_indication
|
$info['mpeg']['video']['raw']['profile_and_level_indication'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for profile_and_level_indication
|
||||||
$info['mpeg']['video']['raw']['progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_sequence
|
$info['mpeg']['video']['raw']['progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_sequence
|
||||||
$info['mpeg']['video']['raw']['chroma_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for chroma_format
|
$info['mpeg']['video']['raw']['chroma_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for chroma_format
|
||||||
$info['mpeg']['video']['raw']['horizontal_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for horizontal_size_extension
|
$info['mpeg']['video']['raw']['horizontal_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for horizontal_size_extension
|
||||||
$info['mpeg']['video']['raw']['vertical_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for vertical_size_extension
|
$info['mpeg']['video']['raw']['vertical_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for vertical_size_extension
|
||||||
$info['mpeg']['video']['raw']['bit_rate_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for bit_rate_extension
|
$info['mpeg']['video']['raw']['bit_rate_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for bit_rate_extension
|
||||||
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
||||||
$info['mpeg']['video']['raw']['vbv_buffer_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for vbv_buffer_size_extension
|
$info['mpeg']['video']['raw']['vbv_buffer_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for vbv_buffer_size_extension
|
||||||
$info['mpeg']['video']['raw']['low_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: low_delay
|
$info['mpeg']['video']['raw']['low_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: low_delay
|
||||||
$info['mpeg']['video']['raw']['frame_rate_extension_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for frame_rate_extension_n
|
$info['mpeg']['video']['raw']['frame_rate_extension_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for frame_rate_extension_n
|
||||||
$info['mpeg']['video']['raw']['frame_rate_extension_d'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for frame_rate_extension_d
|
$info['mpeg']['video']['raw']['frame_rate_extension_d'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for frame_rate_extension_d
|
||||||
|
|
||||||
$info['video']['resolution_x'] = ($info['mpeg']['video']['raw']['horizontal_size_extension'] << 12) | $info['mpeg']['video']['raw']['horizontal_size_value'];
|
$info['video']['resolution_x'] = ($info['mpeg']['video']['raw']['horizontal_size_extension'] << 12) | $info['mpeg']['video']['raw']['horizontal_size_value'];
|
||||||
$info['video']['resolution_y'] = ($info['mpeg']['video']['raw']['vertical_size_extension'] << 12) | $info['mpeg']['video']['raw']['vertical_size_value'];
|
$info['video']['resolution_y'] = ($info['mpeg']['video']['raw']['vertical_size_extension'] << 12) | $info['mpeg']['video']['raw']['vertical_size_value'];
|
||||||
$info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
|
$info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
|
||||||
$info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
|
$info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
|
||||||
$info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']);
|
$info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // 0010 Sequence Display Extension ID
|
case 2: // 0010 Sequence Display Extension ID
|
||||||
$info['mpeg']['video']['raw']['video_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for video_format
|
$info['mpeg']['video']['raw']['video_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for video_format
|
||||||
$info['mpeg']['video']['raw']['colour_description'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: colour_description
|
$info['mpeg']['video']['raw']['colour_description'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: colour_description
|
||||||
if ($info['mpeg']['video']['raw']['colour_description']) {
|
if ($info['mpeg']['video']['raw']['colour_description']) {
|
||||||
$info['mpeg']['video']['raw']['colour_primaries'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for colour_primaries
|
$info['mpeg']['video']['raw']['colour_primaries'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for colour_primaries
|
||||||
$info['mpeg']['video']['raw']['transfer_characteristics'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for transfer_characteristics
|
$info['mpeg']['video']['raw']['transfer_characteristics'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for transfer_characteristics
|
||||||
$info['mpeg']['video']['raw']['matrix_coefficients'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for matrix_coefficients
|
$info['mpeg']['video']['raw']['matrix_coefficients'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for matrix_coefficients
|
||||||
}
|
}
|
||||||
$info['mpeg']['video']['raw']['display_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_horizontal_size
|
$info['mpeg']['video']['raw']['display_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_horizontal_size
|
||||||
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
||||||
$info['mpeg']['video']['raw']['display_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_vertical_size
|
$info['mpeg']['video']['raw']['display_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_vertical_size
|
||||||
|
|
||||||
$info['mpeg']['video']['video_format'] = self::videoFormatTextLookup($info['mpeg']['video']['raw']['video_format']);
|
$info['mpeg']['video']['video_format'] = self::videoFormatTextLookup($info['mpeg']['video']['raw']['video_format']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: // 0011 Quant Matrix Extension ID
|
case 3: // 0011 Quant Matrix Extension ID
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // 0101 Sequence Scalable Extension ID
|
case 5: // 0101 Sequence Scalable Extension ID
|
||||||
$info['mpeg']['video']['raw']['scalable_mode'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scalable_mode
|
$info['mpeg']['video']['raw']['scalable_mode'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scalable_mode
|
||||||
$info['mpeg']['video']['raw']['layer_id'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for layer_id
|
$info['mpeg']['video']['raw']['layer_id'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for layer_id
|
||||||
if ($info['mpeg']['video']['raw']['scalable_mode'] == 1) { // "spatial scalability"
|
if ($info['mpeg']['video']['raw']['scalable_mode'] == 1) { // "spatial scalability"
|
||||||
$info['mpeg']['video']['raw']['lower_layer_prediction_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_horizontal_size
|
$info['mpeg']['video']['raw']['lower_layer_prediction_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_horizontal_size
|
||||||
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
||||||
$info['mpeg']['video']['raw']['lower_layer_prediction_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_vertical_size
|
$info['mpeg']['video']['raw']['lower_layer_prediction_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_vertical_size
|
||||||
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_m
|
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_m
|
||||||
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_n
|
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_n
|
||||||
$info['mpeg']['video']['raw']['vertical_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_m
|
$info['mpeg']['video']['raw']['vertical_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_m
|
||||||
$info['mpeg']['video']['raw']['vertical_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_n
|
$info['mpeg']['video']['raw']['vertical_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_n
|
||||||
} elseif ($info['mpeg']['video']['raw']['scalable_mode'] == 3) { // "temporal scalability"
|
} elseif ($info['mpeg']['video']['raw']['scalable_mode'] == 3) { // "temporal scalability"
|
||||||
$info['mpeg']['video']['raw']['picture_mux_enable'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: picture_mux_enable
|
$info['mpeg']['video']['raw']['picture_mux_enable'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: picture_mux_enable
|
||||||
if ($info['mpeg']['video']['raw']['picture_mux_enable']) {
|
if ($info['mpeg']['video']['raw']['picture_mux_enable']) {
|
||||||
$info['mpeg']['video']['raw']['mux_to_progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: mux_to_progressive_sequence
|
$info['mpeg']['video']['raw']['mux_to_progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: mux_to_progressive_sequence
|
||||||
}
|
}
|
||||||
$info['mpeg']['video']['raw']['picture_mux_order'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_order
|
$info['mpeg']['video']['raw']['picture_mux_order'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_order
|
||||||
$info['mpeg']['video']['raw']['picture_mux_factor'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_factor
|
$info['mpeg']['video']['raw']['picture_mux_factor'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_factor
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['mpeg']['video']['scalable_mode'] = self::scalableModeTextLookup($info['mpeg']['video']['raw']['scalable_mode']);
|
$info['mpeg']['video']['scalable_mode'] = self::scalableModeTextLookup($info['mpeg']['video']['raw']['scalable_mode']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: // 0111 Picture Display Extension ID
|
case 7: // 0111 Picture Display Extension ID
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: // 1000 Picture Coding Extension ID
|
case 8: // 1000 Picture Coding Extension ID
|
||||||
$info['mpeg']['video']['raw']['f_code_00'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][0] (forward horizontal)
|
$info['mpeg']['video']['raw']['f_code_00'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][0] (forward horizontal)
|
||||||
$info['mpeg']['video']['raw']['f_code_01'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][1] (forward vertical)
|
$info['mpeg']['video']['raw']['f_code_01'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][1] (forward vertical)
|
||||||
$info['mpeg']['video']['raw']['f_code_10'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][0] (backward horizontal)
|
$info['mpeg']['video']['raw']['f_code_10'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][0] (backward horizontal)
|
||||||
$info['mpeg']['video']['raw']['f_code_11'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][1] (backward vertical)
|
$info['mpeg']['video']['raw']['f_code_11'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][1] (backward vertical)
|
||||||
$info['mpeg']['video']['raw']['intra_dc_precision'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for intra_dc_precision
|
$info['mpeg']['video']['raw']['intra_dc_precision'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for intra_dc_precision
|
||||||
$info['mpeg']['video']['raw']['picture_structure'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for picture_structure
|
$info['mpeg']['video']['raw']['picture_structure'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for picture_structure
|
||||||
$info['mpeg']['video']['raw']['top_field_first'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: top_field_first
|
$info['mpeg']['video']['raw']['top_field_first'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: top_field_first
|
||||||
$info['mpeg']['video']['raw']['frame_pred_frame_dct'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: frame_pred_frame_dct
|
$info['mpeg']['video']['raw']['frame_pred_frame_dct'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: frame_pred_frame_dct
|
||||||
$info['mpeg']['video']['raw']['concealment_motion_vectors'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: concealment_motion_vectors
|
$info['mpeg']['video']['raw']['concealment_motion_vectors'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: concealment_motion_vectors
|
||||||
$info['mpeg']['video']['raw']['q_scale_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: q_scale_type
|
$info['mpeg']['video']['raw']['q_scale_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: q_scale_type
|
||||||
$info['mpeg']['video']['raw']['intra_vlc_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: intra_vlc_format
|
$info['mpeg']['video']['raw']['intra_vlc_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: intra_vlc_format
|
||||||
$info['mpeg']['video']['raw']['alternate_scan'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: alternate_scan
|
$info['mpeg']['video']['raw']['alternate_scan'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: alternate_scan
|
||||||
$info['mpeg']['video']['raw']['repeat_first_field'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: repeat_first_field
|
$info['mpeg']['video']['raw']['repeat_first_field'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: repeat_first_field
|
||||||
$info['mpeg']['video']['raw']['chroma_420_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: chroma_420_type
|
$info['mpeg']['video']['raw']['chroma_420_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: chroma_420_type
|
||||||
$info['mpeg']['video']['raw']['progressive_frame'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_frame
|
$info['mpeg']['video']['raw']['progressive_frame'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_frame
|
||||||
$info['mpeg']['video']['raw']['composite_display_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: composite_display_flag
|
$info['mpeg']['video']['raw']['composite_display_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: composite_display_flag
|
||||||
if ($info['mpeg']['video']['raw']['composite_display_flag']) {
|
if ($info['mpeg']['video']['raw']['composite_display_flag']) {
|
||||||
$info['mpeg']['video']['raw']['v_axis'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: v_axis
|
$info['mpeg']['video']['raw']['v_axis'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: v_axis
|
||||||
$info['mpeg']['video']['raw']['field_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for field_sequence
|
$info['mpeg']['video']['raw']['field_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for field_sequence
|
||||||
$info['mpeg']['video']['raw']['sub_carrier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: sub_carrier
|
$info['mpeg']['video']['raw']['sub_carrier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: sub_carrier
|
||||||
$info['mpeg']['video']['raw']['burst_amplitude'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 7); // 7 bits for burst_amplitude
|
$info['mpeg']['video']['raw']['burst_amplitude'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 7); // 7 bits for burst_amplitude
|
||||||
$info['mpeg']['video']['raw']['sub_carrier_phase'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for sub_carrier_phase
|
$info['mpeg']['video']['raw']['sub_carrier_phase'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for sub_carrier_phase
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['mpeg']['video']['intra_dc_precision_bits'] = $info['mpeg']['video']['raw']['intra_dc_precision'] + 8;
|
$info['mpeg']['video']['intra_dc_precision_bits'] = $info['mpeg']['video']['raw']['intra_dc_precision'] + 8;
|
||||||
$info['mpeg']['video']['picture_structure'] = self::pictureStructureTextLookup($info['mpeg']['video']['raw']['picture_structure']);
|
$info['mpeg']['video']['picture_structure'] = self::pictureStructureTextLookup($info['mpeg']['video']['raw']['picture_structure']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 9: // 1001 Picture Spatial Scalable Extension ID
|
case 9: // 1001 Picture Spatial Scalable Extension ID
|
||||||
break;
|
break;
|
||||||
case 10: // 1010 Picture Temporal Scalable Extension ID
|
case 10: // 1010 Picture Temporal Scalable Extension ID
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->warning('Unexpected $info[mpeg][video][raw][extension_start_code_identifier] value of '.$info['mpeg']['video']['raw']['extension_start_code_identifier']);
|
$this->warning('Unexpected $info[mpeg][video][raw][extension_start_code_identifier] value of '.$info['mpeg']['video']['raw']['extension_start_code_identifier']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0xB8: // group_of_pictures_header
|
case 0xB8: // group_of_pictures_header
|
||||||
$GOPcounter++;
|
$GOPcounter++;
|
||||||
if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
|
if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
|
||||||
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); // 27 bits needed for group_of_pictures_header
|
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); // 27 bits needed for group_of_pictures_header
|
||||||
$bitstreamoffset = 0;
|
$bitstreamoffset = 0;
|
||||||
|
|
||||||
$GOPheader = [];
|
$GOPheader = array();
|
||||||
|
|
||||||
$GOPheader['byte_offset'] = $MPEGstreamBaseOffset + $StartCodeOffset;
|
$GOPheader['byte_offset'] = $MPEGstreamBaseOffset + $StartCodeOffset;
|
||||||
$GOPheader['drop_frame_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: drop_frame_flag
|
$GOPheader['drop_frame_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: drop_frame_flag
|
||||||
$GOPheader['time_code_hours'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for time_code_hours
|
$GOPheader['time_code_hours'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for time_code_hours
|
||||||
$GOPheader['time_code_minutes'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_minutes
|
$GOPheader['time_code_minutes'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_minutes
|
||||||
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
|
||||||
$GOPheader['time_code_seconds'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_seconds
|
$GOPheader['time_code_seconds'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_seconds
|
||||||
$GOPheader['time_code_pictures'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_pictures
|
$GOPheader['time_code_pictures'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_pictures
|
||||||
$GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: closed_gop
|
$GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: closed_gop
|
||||||
$GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: broken_link
|
$GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: broken_link
|
||||||
|
|
||||||
$time_code_separator = ($GOPheader['drop_frame_flag'] ? ';' : ':'); // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF"
|
$time_code_separator = ($GOPheader['drop_frame_flag'] ? ';' : ':'); // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF"
|
||||||
$GOPheader['time_code'] = sprintf('%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']);
|
$GOPheader['time_code'] = sprintf('%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']);
|
||||||
|
|
||||||
$info['mpeg']['group_of_pictures'][] = $GOPheader;
|
$info['mpeg']['group_of_pictures'][] = $GOPheader;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xC0: // audio stream
|
case 0xC0: // audio stream
|
||||||
case 0xC1: // audio stream
|
case 0xC1: // audio stream
|
||||||
case 0xC2: // audio stream
|
case 0xC2: // audio stream
|
||||||
case 0xC3: // audio stream
|
case 0xC3: // audio stream
|
||||||
case 0xC4: // audio stream
|
case 0xC4: // audio stream
|
||||||
case 0xC5: // audio stream
|
case 0xC5: // audio stream
|
||||||
case 0xC6: // audio stream
|
case 0xC6: // audio stream
|
||||||
case 0xC7: // audio stream
|
case 0xC7: // audio stream
|
||||||
case 0xC8: // audio stream
|
case 0xC8: // audio stream
|
||||||
case 0xC9: // audio stream
|
case 0xC9: // audio stream
|
||||||
case 0xCA: // audio stream
|
case 0xCA: // audio stream
|
||||||
case 0xCB: // audio stream
|
case 0xCB: // audio stream
|
||||||
case 0xCC: // audio stream
|
case 0xCC: // audio stream
|
||||||
case 0xCD: // audio stream
|
case 0xCD: // audio stream
|
||||||
case 0xCE: // audio stream
|
case 0xCE: // audio stream
|
||||||
case 0xCF: // audio stream
|
case 0xCF: // audio stream
|
||||||
case 0xD0: // audio stream
|
case 0xD0: // audio stream
|
||||||
case 0xD1: // audio stream
|
case 0xD1: // audio stream
|
||||||
case 0xD2: // audio stream
|
case 0xD2: // audio stream
|
||||||
case 0xD3: // audio stream
|
case 0xD3: // audio stream
|
||||||
case 0xD4: // audio stream
|
case 0xD4: // audio stream
|
||||||
case 0xD5: // audio stream
|
case 0xD5: // audio stream
|
||||||
case 0xD6: // audio stream
|
case 0xD6: // audio stream
|
||||||
case 0xD7: // audio stream
|
case 0xD7: // audio stream
|
||||||
case 0xD8: // audio stream
|
case 0xD8: // audio stream
|
||||||
case 0xD9: // audio stream
|
case 0xD9: // audio stream
|
||||||
case 0xDA: // audio stream
|
case 0xDA: // audio stream
|
||||||
case 0xDB: // audio stream
|
case 0xDB: // audio stream
|
||||||
case 0xDC: // audio stream
|
case 0xDC: // audio stream
|
||||||
case 0xDD: // audio stream
|
case 0xDD: // audio stream
|
||||||
case 0xDE: // audio stream
|
case 0xDE: // audio stream
|
||||||
case 0xDF: // audio stream
|
case 0xDF: // audio stream
|
||||||
//case 0xE0: // video stream
|
//case 0xE0: // video stream
|
||||||
//case 0xE1: // video stream
|
//case 0xE1: // video stream
|
||||||
//case 0xE2: // video stream
|
//case 0xE2: // video stream
|
||||||
//case 0xE3: // video stream
|
//case 0xE3: // video stream
|
||||||
//case 0xE4: // video stream
|
//case 0xE4: // video stream
|
||||||
//case 0xE5: // video stream
|
//case 0xE5: // video stream
|
||||||
//case 0xE6: // video stream
|
//case 0xE6: // video stream
|
||||||
//case 0xE7: // video stream
|
//case 0xE7: // video stream
|
||||||
//case 0xE8: // video stream
|
//case 0xE8: // video stream
|
||||||
//case 0xE9: // video stream
|
//case 0xE9: // video stream
|
||||||
//case 0xEA: // video stream
|
//case 0xEA: // video stream
|
||||||
//case 0xEB: // video stream
|
//case 0xEB: // video stream
|
||||||
//case 0xEC: // video stream
|
//case 0xEC: // video stream
|
||||||
//case 0xED: // video stream
|
//case 0xED: // video stream
|
||||||
//case 0xEE: // video stream
|
//case 0xEE: // video stream
|
||||||
//case 0xEF: // video stream
|
//case 0xEF: // video stream
|
||||||
if (isset($ParsedAVchannels[$StartCodeValue])) {
|
if (isset($ParsedAVchannels[$StartCodeValue])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$ParsedAVchannels[$StartCodeValue] = $StartCodeValue;
|
$ParsedAVchannels[$StartCodeValue] = $StartCodeValue;
|
||||||
// http://en.wikipedia.org/wiki/Packetized_elementary_stream
|
// http://en.wikipedia.org/wiki/Packetized_elementary_stream
|
||||||
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
|
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
|
||||||
/*
|
/*
|
||||||
$PackedElementaryStream = array();
|
$PackedElementaryStream = array();
|
||||||
if ($StartCodeValue >= 0xE0) {
|
if ($StartCodeValue >= 0xE0) {
|
||||||
|
@ -380,55 +379,55 @@ $PackedElementaryStream['additional_header_bytes'] = $additional_header_bytes;
|
||||||
|
|
||||||
$info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream;
|
$info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream;
|
||||||
*/
|
*/
|
||||||
$getid3_temp = new getID3();
|
$getid3_temp = new getID3();
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
$getid3_temp->info = $info;
|
$getid3_temp->info = $info;
|
||||||
$getid3_mp3 = new getid3_mp3($getid3_temp);
|
$getid3_mp3 = new getid3_mp3($getid3_temp);
|
||||||
for ($i = 0; $i <= 7; $i++) {
|
for ($i = 0; $i <= 7; $i++) {
|
||||||
// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
|
// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
|
||||||
// I have no idea why or what the difference is, so this is a stupid hack.
|
// I have no idea why or what the difference is, so this is a stupid hack.
|
||||||
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
|
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
|
||||||
$getid3_temp->info = $info; // only overwrite real data if valid header found
|
$getid3_temp->info = $info; // only overwrite real data if valid header found
|
||||||
//echo 'audio at? '.($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i).'<br>';
|
//echo 'audio at? '.($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i).'<br>';
|
||||||
if ($getid3_mp3->decodeMPEGaudioHeader($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i, $getid3_temp->info, false)) {
|
if ($getid3_mp3->decodeMPEGaudioHeader($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i, $getid3_temp->info, false)) {
|
||||||
//echo 'yes!<br>';
|
//echo 'yes!<br>';
|
||||||
$info = $getid3_temp->info;
|
$info = $getid3_temp->info;
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($getid3_temp, $getid3_mp3);
|
unset($getid3_temp, $getid3_mp3);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xBC: // Program Stream Map
|
case 0xBC: // Program Stream Map
|
||||||
case 0xBD: // Private stream 1 (non MPEG audio, subpictures)
|
case 0xBD: // Private stream 1 (non MPEG audio, subpictures)
|
||||||
case 0xBE: // Padding stream
|
case 0xBE: // Padding stream
|
||||||
case 0xBF: // Private stream 2 (navigation data)
|
case 0xBF: // Private stream 2 (navigation data)
|
||||||
case 0xF0: // ECM stream
|
case 0xF0: // ECM stream
|
||||||
case 0xF1: // EMM stream
|
case 0xF1: // EMM stream
|
||||||
case 0xF2: // DSM-CC stream
|
case 0xF2: // DSM-CC stream
|
||||||
case 0xF3: // ISO/IEC_13522_stream
|
case 0xF3: // ISO/IEC_13522_stream
|
||||||
case 0xF4: // ITU-I Rec. H.222.1 type A
|
case 0xF4: // ITU-I Rec. H.222.1 type A
|
||||||
case 0xF5: // ITU-I Rec. H.222.1 type B
|
case 0xF5: // ITU-I Rec. H.222.1 type B
|
||||||
case 0xF6: // ITU-I Rec. H.222.1 type C
|
case 0xF6: // ITU-I Rec. H.222.1 type C
|
||||||
case 0xF7: // ITU-I Rec. H.222.1 type D
|
case 0xF7: // ITU-I Rec. H.222.1 type D
|
||||||
case 0xF8: // ITU-I Rec. H.222.1 type E
|
case 0xF8: // ITU-I Rec. H.222.1 type E
|
||||||
case 0xF9: // ancilliary stream
|
case 0xF9: // ancilliary stream
|
||||||
case 0xFA: // ISO/IEC 14496-1 SL-packtized stream
|
case 0xFA: // ISO/IEC 14496-1 SL-packtized stream
|
||||||
case 0xFB: // ISO/IEC 14496-1 FlexMux stream
|
case 0xFB: // ISO/IEC 14496-1 FlexMux stream
|
||||||
case 0xFC: // metadata stream
|
case 0xFC: // metadata stream
|
||||||
case 0xFD: // extended stream ID
|
case 0xFD: // extended stream ID
|
||||||
case 0xFE: // reserved data stream
|
case 0xFE: // reserved data stream
|
||||||
case 0xFF: // program stream directory
|
case 0xFF: // program stream directory
|
||||||
// ignore
|
// ignore
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// ignore
|
// ignore
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -460,7 +459,7 @@ $PackedElementaryStream['additional_header_bytes'] = $additional_header_bytes;
|
||||||
// // break;
|
// // break;
|
||||||
// //}
|
// //}
|
||||||
// //$info['playtime_seconds'] *= $multiplier;
|
// //$info['playtime_seconds'] *= $multiplier;
|
||||||
// //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
|
// //$this->warning('Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.');
|
||||||
// if ($info['video']['bitrate'] < 50000) {
|
// if ($info['video']['bitrate'] < 50000) {
|
||||||
// $this->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.');
|
// $this->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.');
|
||||||
// }
|
// }
|
||||||
|
@ -488,128 +487,120 @@ echo $gopkey.': ('.number_format($time_prev, 2).'-'.number_format($time_this, 2)
|
||||||
echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($vbr_bitrates), 1).'<br>';
|
echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($vbr_bitrates), 1).'<br>';
|
||||||
*/
|
*/
|
||||||
//echo '<pre>'.print_r($FramesByGOP, true).'</pre>';
|
//echo '<pre>'.print_r($FramesByGOP, true).'</pre>';
|
||||||
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
|
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
|
||||||
$last_GOP_id = max(array_keys($FramesByGOP));
|
$last_GOP_id = max(array_keys($FramesByGOP));
|
||||||
$frames_in_last_GOP = count($FramesByGOP[$last_GOP_id]);
|
$frames_in_last_GOP = count($FramesByGOP[$last_GOP_id]);
|
||||||
$gopdata = &$info['mpeg']['group_of_pictures'][$last_GOP_id];
|
$gopdata = &$info['mpeg']['group_of_pictures'][$last_GOP_id];
|
||||||
$info['playtime_seconds'] = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + (($gopdata['time_code_pictures'] + $frames_in_last_GOP + 1) / $info['mpeg']['video']['frame_rate']);
|
$info['playtime_seconds'] = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + (($gopdata['time_code_pictures'] + $frames_in_last_GOP + 1) / $info['mpeg']['video']['frame_rate']);
|
||||||
if (!isset($info['video']['bitrate'])) {
|
if (!isset($info['video']['bitrate'])) {
|
||||||
$overall_bitrate = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
|
$overall_bitrate = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
|
||||||
$info['video']['bitrate'] = $overall_bitrate - (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
|
$info['video']['bitrate'] = $overall_bitrate - (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
|
||||||
}
|
}
|
||||||
unset($info['mpeg']['group_of_pictures']);
|
unset($info['mpeg']['group_of_pictures']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function readBitsFromStream(&$bitstream, &$bitstreamoffset, $bits_to_read, $return_singlebit_as_boolean = true)
|
private function readBitsFromStream(&$bitstream, &$bitstreamoffset, $bits_to_read, $return_singlebit_as_boolean=true) {
|
||||||
{
|
$return = bindec(substr($bitstream, $bitstreamoffset, $bits_to_read));
|
||||||
$return = bindec(substr($bitstream, $bitstreamoffset, $bits_to_read));
|
$bitstreamoffset += $bits_to_read;
|
||||||
$bitstreamoffset += $bits_to_read;
|
if (($bits_to_read == 1) && $return_singlebit_as_boolean) {
|
||||||
if (($bits_to_read == 1) && $return_singlebit_as_boolean) {
|
$return = (bool) $return;
|
||||||
$return = (bool) $return;
|
}
|
||||||
}
|
return $return;
|
||||||
return $return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function systemNonOverheadPercentage($VideoBitrate, $AudioBitrate)
|
public static function systemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
|
||||||
{
|
$OverheadPercentage = 0;
|
||||||
$OverheadPercentage = 0;
|
|
||||||
|
|
||||||
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
|
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
|
||||||
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
|
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
|
||||||
|
|
||||||
|
|
||||||
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
|
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
|
||||||
$OverheadMultiplierByBitrate[32] = [0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940];
|
$OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
|
||||||
$OverheadMultiplierByBitrate[48] = [0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960];
|
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
|
||||||
$OverheadMultiplierByBitrate[56] = [0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340];
|
$OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
|
||||||
$OverheadMultiplierByBitrate[64] = [0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470];
|
$OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
|
||||||
$OverheadMultiplierByBitrate[96] = [0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690];
|
$OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
|
||||||
$OverheadMultiplierByBitrate[128] = [0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050];
|
$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
|
||||||
$OverheadMultiplierByBitrate[160] = [0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570];
|
$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
|
||||||
$OverheadMultiplierByBitrate[192] = [0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620];
|
$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
|
||||||
$OverheadMultiplierByBitrate[224] = [0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480];
|
$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
|
||||||
$OverheadMultiplierByBitrate[256] = [0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790];
|
$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
|
||||||
$OverheadMultiplierByBitrate[320] = [0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190];
|
$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
|
||||||
$OverheadMultiplierByBitrate[384] = [0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890];
|
$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
|
||||||
|
|
||||||
$BitrateToUseMin = 32;
|
$BitrateToUseMin = 32;
|
||||||
$BitrateToUseMax = 32;
|
$BitrateToUseMax = 32;
|
||||||
$previousBitrate = 32;
|
$previousBitrate = 32;
|
||||||
foreach ($OverheadMultiplierByBitrate as $key => $value) {
|
foreach ($OverheadMultiplierByBitrate as $key => $value) {
|
||||||
if ($AudioBitrate >= $previousBitrate) {
|
if ($AudioBitrate >= $previousBitrate) {
|
||||||
$BitrateToUseMin = $previousBitrate;
|
$BitrateToUseMin = $previousBitrate;
|
||||||
}
|
}
|
||||||
if ($AudioBitrate < $key) {
|
if ($AudioBitrate < $key) {
|
||||||
$BitrateToUseMax = $key;
|
$BitrateToUseMax = $key;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$previousBitrate = $key;
|
$previousBitrate = $key;
|
||||||
}
|
}
|
||||||
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
|
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
|
||||||
|
|
||||||
$VideoBitrateLog10 = log10($VideoBitrate);
|
$VideoBitrateLog10 = log10($VideoBitrate);
|
||||||
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
|
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
|
||||||
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
|
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
|
||||||
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
|
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
|
||||||
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
|
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
|
||||||
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
|
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
|
||||||
|
|
||||||
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
|
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
|
||||||
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
|
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
|
||||||
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
|
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
|
||||||
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
|
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
|
||||||
|
|
||||||
return $OverheadPercentage;
|
return $OverheadPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function videoFramerateLookup($rawframerate)
|
public static function videoFramerateLookup($rawframerate) {
|
||||||
{
|
$lookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
|
||||||
$lookup = [0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60];
|
return (isset($lookup[$rawframerate]) ? (float) $lookup[$rawframerate] : (float) 0);
|
||||||
return (isset($lookup[$rawframerate]) ? (float) $lookup[$rawframerate] : (float) 0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function videoAspectRatioLookup($rawaspectratio)
|
public static function videoAspectRatioLookup($rawaspectratio) {
|
||||||
{
|
$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);
|
||||||
$lookup = [0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0];
|
return (isset($lookup[$rawaspectratio]) ? (float) $lookup[$rawaspectratio] : (float) 0);
|
||||||
return (isset($lookup[$rawaspectratio]) ? (float) $lookup[$rawaspectratio] : (float) 0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function videoAspectRatioTextLookup($rawaspectratio)
|
public static function videoAspectRatioTextLookup($rawaspectratio) {
|
||||||
{
|
$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');
|
||||||
$lookup = ['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[$rawaspectratio]) ? $lookup[$rawaspectratio] : '');
|
||||||
return (isset($lookup[$rawaspectratio]) ? $lookup[$rawaspectratio] : '');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function videoFormatTextLookup($video_format)
|
public static function videoFormatTextLookup($video_format) {
|
||||||
{
|
// ISO/IEC 13818-2, section 6.3.6, Table 6-6. Meaning of video_format
|
||||||
// ISO/IEC 13818-2, section 6.3.6, Table 6-6. Meaning of video_format
|
$lookup = array('component', 'PAL', 'NTSC', 'SECAM', 'MAC', 'Unspecified video format', 'reserved(6)', 'reserved(7)');
|
||||||
$lookup = ['component', 'PAL', 'NTSC', 'SECAM', 'MAC', 'Unspecified video format', 'reserved(6)', 'reserved(7)'];
|
return (isset($lookup[$video_format]) ? $lookup[$video_format] : '');
|
||||||
return (isset($lookup[$video_format]) ? $lookup[$video_format] : '');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function scalableModeTextLookup($scalable_mode)
|
public static function scalableModeTextLookup($scalable_mode) {
|
||||||
{
|
// ISO/IEC 13818-2, section 6.3.8, Table 6-10. Definition of scalable_mode
|
||||||
// ISO/IEC 13818-2, section 6.3.8, Table 6-10. Definition of scalable_mode
|
$lookup = array('data partitioning', 'spatial scalability', 'SNR scalability', 'temporal scalability');
|
||||||
$lookup = ['data partitioning', 'spatial scalability', 'SNR scalability', 'temporal scalability'];
|
return (isset($lookup[$scalable_mode]) ? $lookup[$scalable_mode] : '');
|
||||||
return (isset($lookup[$scalable_mode]) ? $lookup[$scalable_mode] : '');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function pictureStructureTextLookup($picture_structure)
|
public static function pictureStructureTextLookup($picture_structure) {
|
||||||
{
|
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
|
||||||
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
|
$lookup = array('reserved', 'Top Field', 'Bottom Field', 'Frame picture');
|
||||||
$lookup = ['reserved', 'Top Field', 'Bottom Field', 'Frame picture'];
|
return (isset($lookup[$picture_structure]) ? $lookup[$picture_structure] : '');
|
||||||
return (isset($lookup[$picture_structure]) ? $lookup[$picture_structure] : '');
|
}
|
||||||
}
|
|
||||||
|
public static function chromaFormatTextLookup($chroma_format) {
|
||||||
|
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
|
||||||
|
$lookup = array('reserved', '4:2:0', '4:2:2', '4:4:4');
|
||||||
|
return (isset($lookup[$chroma_format]) ? $lookup[$chroma_format] : '');
|
||||||
|
}
|
||||||
|
|
||||||
public static function chromaFormatTextLookup($chroma_format)
|
|
||||||
{
|
|
||||||
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
|
|
||||||
$lookup = ['reserved', '4:2:0', '4:2:2', '4:4:4'];
|
|
||||||
return (isset($lookup[$chroma_format]) ? $lookup[$chroma_format] : '');
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -18,210 +18,207 @@
|
||||||
class getid3_nsv extends getid3_handler
|
class getid3_nsv extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$NSVheader = $this->fread(4);
|
$NSVheader = $this->fread(4);
|
||||||
|
|
||||||
switch ($NSVheader) {
|
switch ($NSVheader) {
|
||||||
case 'NSVs':
|
case 'NSVs':
|
||||||
if ($this->getNSVsHeaderFilepointer(0)) {
|
if ($this->getNSVsHeaderFilepointer(0)) {
|
||||||
$info['fileformat'] = 'nsv';
|
$info['fileformat'] = 'nsv';
|
||||||
$info['audio']['dataformat'] = 'nsv';
|
$info['audio']['dataformat'] = 'nsv';
|
||||||
$info['video']['dataformat'] = 'nsv';
|
$info['video']['dataformat'] = 'nsv';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'NSVf':
|
case 'NSVf':
|
||||||
if ($this->getNSVfHeaderFilepointer(0)) {
|
if ($this->getNSVfHeaderFilepointer(0)) {
|
||||||
$info['fileformat'] = 'nsv';
|
$info['fileformat'] = 'nsv';
|
||||||
$info['audio']['dataformat'] = 'nsv';
|
$info['audio']['dataformat'] = 'nsv';
|
||||||
$info['video']['dataformat'] = 'nsv';
|
$info['video']['dataformat'] = 'nsv';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
$this->getNSVsHeaderFilepointer($info['nsv']['NSVf']['header_length']);
|
$this->getNSVsHeaderFilepointer($info['nsv']['NSVf']['header_length']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$info['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"';
|
$this->error('Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"');
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($info['nsv']['NSVf'])) {
|
if (!isset($info['nsv']['NSVf'])) {
|
||||||
$info['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
|
$this->warning('NSVf header not present - cannot calculate playtime or bitrate');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNSVsHeaderFilepointer($fileoffset)
|
public function getNSVsHeaderFilepointer($fileoffset) {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($fileoffset);
|
||||||
$this->fseek($fileoffset);
|
$NSVsheader = $this->fread(28);
|
||||||
$NSVsheader = $this->fread(28);
|
$offset = 0;
|
||||||
$offset = 0;
|
|
||||||
|
|
||||||
$info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
|
$info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
if ($info['nsv']['NSVs']['identifier'] != 'NSVs') {
|
if ($info['nsv']['NSVs']['identifier'] != 'NSVs') {
|
||||||
$info['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVs']['identifier'].'" instead';
|
$this->error('expected "NSVs" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVs']['identifier'].'" instead');
|
||||||
unset($info['nsv']['NSVs']);
|
unset($info['nsv']['NSVs']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
||||||
|
|
||||||
$info['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
|
$info['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
|
$info['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
$info['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$info['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
$info['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
|
|
||||||
$info['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
$info['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
//$info['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
//$info['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
|
|
||||||
switch ($info['nsv']['NSVs']['audio_codec']) {
|
switch ($info['nsv']['NSVs']['audio_codec']) {
|
||||||
case 'PCM ':
|
case 'PCM ':
|
||||||
$info['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
$info['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$info['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
$info['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$info['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
$info['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
|
|
||||||
$info['audio']['sample_rate'] = $info['nsv']['NSVs']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['nsv']['NSVs']['sample_rate'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'MP3 ':
|
case 'MP3 ':
|
||||||
case 'NONE':
|
case 'NONE':
|
||||||
default:
|
default:
|
||||||
//$info['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
|
//$info['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['video']['resolution_x'] = $info['nsv']['NSVs']['resolution_x'];
|
$info['video']['resolution_x'] = $info['nsv']['NSVs']['resolution_x'];
|
||||||
$info['video']['resolution_y'] = $info['nsv']['NSVs']['resolution_y'];
|
$info['video']['resolution_y'] = $info['nsv']['NSVs']['resolution_y'];
|
||||||
$info['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($info['nsv']['NSVs']['framerate_index']);
|
$info['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($info['nsv']['NSVs']['framerate_index']);
|
||||||
$info['video']['frame_rate'] = $info['nsv']['NSVs']['frame_rate'];
|
$info['video']['frame_rate'] = $info['nsv']['NSVs']['frame_rate'];
|
||||||
$info['video']['bits_per_sample'] = 24;
|
$info['video']['bits_per_sample'] = 24;
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets = false)
|
public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($fileoffset);
|
||||||
$this->fseek($fileoffset);
|
$NSVfheader = $this->fread(28);
|
||||||
$NSVfheader = $this->fread(28);
|
$offset = 0;
|
||||||
$offset = 0;
|
|
||||||
|
|
||||||
$info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
|
$info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
if ($info['nsv']['NSVf']['identifier'] != 'NSVf') {
|
if ($info['nsv']['NSVf']['identifier'] != 'NSVf') {
|
||||||
$info['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVf']['identifier'].'" instead';
|
$this->error('expected "NSVf" at offset ('.$fileoffset.'), found "'.$info['nsv']['NSVf']['identifier'].'" instead');
|
||||||
unset($info['nsv']['NSVf']);
|
unset($info['nsv']['NSVf']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
$info['nsv']['NSVs']['offset'] = $fileoffset;
|
||||||
|
|
||||||
$info['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) {
|
if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) {
|
||||||
$info['warning'][] = 'truncated file - NSVf header indicates '.$info['nsv']['NSVf']['file_size'].' bytes, file actually '.$info['avdataend'].' bytes';
|
$this->warning('truncated file - NSVf header indicates '.$info['nsv']['NSVf']['file_size'].' bytes, file actually '.$info['avdataend'].' bytes');
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$info['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
|
if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
|
||||||
$info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
|
$this->error('Corrupt NSV file: NSVf.playtime_ms == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2']));
|
$NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2']));
|
||||||
$NSVfheaderlength = strlen($NSVfheader);
|
$NSVfheaderlength = strlen($NSVfheader);
|
||||||
$info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
|
$info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
|
||||||
$offset += $info['nsv']['NSVf']['meta_size'];
|
$offset += $info['nsv']['NSVf']['meta_size'];
|
||||||
|
|
||||||
if ($getTOCoffsets) {
|
if ($getTOCoffsets) {
|
||||||
$TOCcounter = 0;
|
$TOCcounter = 0;
|
||||||
while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
|
while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
|
||||||
if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
|
if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
|
||||||
$info['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
$info['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$TOCcounter++;
|
$TOCcounter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trim($info['nsv']['NSVf']['metadata']) != '') {
|
if (trim($info['nsv']['NSVf']['metadata']) != '') {
|
||||||
$info['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $info['nsv']['NSVf']['metadata']);
|
$info['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $info['nsv']['NSVf']['metadata']);
|
||||||
$CommentPairArray = explode("\x01".' ', $info['nsv']['NSVf']['metadata']);
|
$CommentPairArray = explode("\x01".' ', $info['nsv']['NSVf']['metadata']);
|
||||||
foreach ($CommentPairArray as $CommentPair) {
|
foreach ($CommentPairArray as $CommentPair) {
|
||||||
if (strstr($CommentPair, '='."\x01")) {
|
if (strstr($CommentPair, '='."\x01")) {
|
||||||
list($key, $value) = explode('='."\x01", $CommentPair, 2);
|
list($key, $value) = explode('='."\x01", $CommentPair, 2);
|
||||||
$info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
|
$info['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
|
$info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
|
||||||
$info['bitrate'] = ($info['nsv']['NSVf']['file_size'] * 8) / $info['playtime_seconds'];
|
$info['bitrate'] = ($info['nsv']['NSVf']['file_size'] * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function NSVframerateLookup($framerateindex)
|
public static function NSVframerateLookup($framerateindex) {
|
||||||
{
|
if ($framerateindex <= 127) {
|
||||||
if ($framerateindex <= 127) {
|
return (float) $framerateindex;
|
||||||
return (float) $framerateindex;
|
}
|
||||||
}
|
static $NSVframerateLookup = array();
|
||||||
static $NSVframerateLookup = [];
|
if (empty($NSVframerateLookup)) {
|
||||||
if (empty($NSVframerateLookup)) {
|
$NSVframerateLookup[129] = (float) 29.970;
|
||||||
$NSVframerateLookup[129] = (float) 29.970;
|
$NSVframerateLookup[131] = (float) 23.976;
|
||||||
$NSVframerateLookup[131] = (float) 23.976;
|
$NSVframerateLookup[133] = (float) 14.985;
|
||||||
$NSVframerateLookup[133] = (float) 14.985;
|
$NSVframerateLookup[197] = (float) 59.940;
|
||||||
$NSVframerateLookup[197] = (float) 59.940;
|
$NSVframerateLookup[199] = (float) 47.952;
|
||||||
$NSVframerateLookup[199] = (float) 47.952;
|
}
|
||||||
}
|
return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
|
||||||
return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,496 +19,510 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
|
||||||
class getid3_real extends getid3_handler
|
class getid3_real extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'real';
|
$info['fileformat'] = 'real';
|
||||||
$info['bitrate'] = 0;
|
$info['bitrate'] = 0;
|
||||||
$info['playtime_seconds'] = 0;
|
$info['playtime_seconds'] = 0;
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$ChunkCounter = 0;
|
$ChunkCounter = 0;
|
||||||
while ($this->ftell() < $info['avdataend']) {
|
while ($this->ftell() < $info['avdataend']) {
|
||||||
$ChunkData = $this->fread(8);
|
$ChunkData = $this->fread(8);
|
||||||
$ChunkName = substr($ChunkData, 0, 4);
|
$ChunkName = substr($ChunkData, 0, 4);
|
||||||
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
|
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
|
||||||
|
|
||||||
if ($ChunkName == '.ra'."\xFD") {
|
if ($ChunkName == '.ra'."\xFD") {
|
||||||
$ChunkData .= $this->fread($ChunkSize - 8);
|
$ChunkData .= $this->fread($ChunkSize - 8);
|
||||||
if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) {
|
if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) {
|
||||||
$info['audio']['dataformat'] = 'real';
|
$info['audio']['dataformat'] = 'real';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
$info['audio']['sample_rate'] = $info['real']['old_ra_header']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['real']['old_ra_header']['sample_rate'];
|
||||||
$info['audio']['bits_per_sample'] = $info['real']['old_ra_header']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['real']['old_ra_header']['bits_per_sample'];
|
||||||
$info['audio']['channels'] = $info['real']['old_ra_header']['channels'];
|
$info['audio']['channels'] = $info['real']['old_ra_header']['channels'];
|
||||||
|
|
||||||
$info['playtime_seconds'] = 60 * ($info['real']['old_ra_header']['audio_bytes'] / $info['real']['old_ra_header']['bytes_per_minute']);
|
$info['playtime_seconds'] = 60 * ($info['real']['old_ra_header']['audio_bytes'] / $info['real']['old_ra_header']['bytes_per_minute']);
|
||||||
$info['audio']['bitrate'] = 8 * ($info['real']['old_ra_header']['audio_bytes'] / $info['playtime_seconds']);
|
$info['audio']['bitrate'] = 8 * ($info['real']['old_ra_header']['audio_bytes'] / $info['playtime_seconds']);
|
||||||
$info['audio']['codec'] = $this->RealAudioCodecFourCClookup($info['real']['old_ra_header']['fourcc'], $info['audio']['bitrate']);
|
$info['audio']['codec'] = $this->RealAudioCodecFourCClookup($info['real']['old_ra_header']['fourcc'], $info['audio']['bitrate']);
|
||||||
|
|
||||||
foreach ($info['real']['old_ra_header']['comments'] as $key => $valuearray) {
|
foreach ($info['real']['old_ra_header']['comments'] as $key => $valuearray) {
|
||||||
if (strlen(trim($valuearray[0])) > 0) {
|
if (strlen(trim($valuearray[0])) > 0) {
|
||||||
$info['real']['comments'][$key][] = trim($valuearray[0]);
|
$info['real']['comments'][$key][] = trim($valuearray[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$info['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org';
|
$this->error('There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org');
|
||||||
unset($info['bitrate']);
|
unset($info['bitrate']);
|
||||||
unset($info['playtime_seconds']);
|
unset($info['playtime_seconds']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['real']['chunks'][$ChunkCounter] = [];
|
$info['real']['chunks'][$ChunkCounter] = array();
|
||||||
$thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter];
|
$thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk['name'] = $ChunkName;
|
$thisfile_real_chunks_currentchunk['name'] = $ChunkName;
|
||||||
$thisfile_real_chunks_currentchunk['offset'] = $this->ftell() - 8;
|
$thisfile_real_chunks_currentchunk['offset'] = $this->ftell() - 8;
|
||||||
$thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
|
$thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
|
||||||
if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) {
|
if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) {
|
||||||
$info['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file';
|
$this->warning('Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) {
|
if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) {
|
||||||
$ChunkData .= $this->fread($this->getid3->fread_buffer_size() - 8);
|
|
||||||
$this->fseek($thisfile_real_chunks_currentchunk['offset'] + $ChunkSize);
|
|
||||||
} elseif (($ChunkSize - 8) > 0) {
|
|
||||||
$ChunkData .= $this->fread($ChunkSize - 8);
|
|
||||||
}
|
|
||||||
$offset = 8;
|
|
||||||
|
|
||||||
switch ($ChunkName) {
|
$ChunkData .= $this->fread($this->getid3->fread_buffer_size() - 8);
|
||||||
case '.RMF': // RealMedia File Header
|
$this->fseek($thisfile_real_chunks_currentchunk['offset'] + $ChunkSize);
|
||||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
|
||||||
$offset += 2;
|
|
||||||
switch ($thisfile_real_chunks_currentchunk['object_version']) {
|
|
||||||
case 0:
|
|
||||||
$thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
|
||||||
$offset += 4;
|
|
||||||
$thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
|
||||||
$offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
} elseif(($ChunkSize - 8) > 0) {
|
||||||
//$info['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)';
|
|
||||||
break;
|
$ChunkData .= $this->fread($ChunkSize - 8);
|
||||||
}
|
|
||||||
break;
|
}
|
||||||
|
$offset = 8;
|
||||||
|
|
||||||
|
switch ($ChunkName) {
|
||||||
|
|
||||||
|
case '.RMF': // RealMedia File Header
|
||||||
|
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
|
$offset += 2;
|
||||||
|
switch ($thisfile_real_chunks_currentchunk['object_version']) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
$thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
|
$offset += 4;
|
||||||
|
$thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
|
$offset += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//$this->warning('Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)');
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 'PROP': // Properties Header
|
case 'PROP': // Properties Header
|
||||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||||
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$info['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
|
$info['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
|
||||||
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
||||||
$info['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
$info['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||||
}
|
}
|
||||||
$thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
|
$thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
|
||||||
$thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
|
$thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
|
||||||
$thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
|
$thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'MDPR': // Media Properties Header
|
case 'MDPR': // Media Properties Header
|
||||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||||
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
$thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
|
$thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
|
$offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
|
||||||
$thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
$thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
|
$thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
|
$offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
|
||||||
$thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
|
$thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
|
$offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
|
$thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
|
||||||
|
|
||||||
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
||||||
case 'video/x-pn-realvideo':
|
case 'video/x-pn-realvideo':
|
||||||
case 'video/x-pn-multirate-realvideo':
|
case 'video/x-pn-multirate-realvideo':
|
||||||
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$thisfile_real_chunks_currentchunk['video_info'] = [];
|
$thisfile_real_chunks_currentchunk['video_info'] = array();
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
|
$thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
|
$thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
|
$thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
|
$thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
|
$thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
|
$thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
|
$thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
|
$thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
|
//$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::fourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
|
$thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::fourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
|
||||||
|
|
||||||
$info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
|
$info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
|
||||||
$info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
|
$info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
|
||||||
$info['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
|
$info['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
|
||||||
$info['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec'];
|
$info['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec'];
|
||||||
$info['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
|
$info['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'audio/x-pn-realaudio':
|
case 'audio/x-pn-realaudio':
|
||||||
case 'audio/x-pn-multirate-realaudio':
|
case 'audio/x-pn-multirate-realaudio':
|
||||||
$this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']);
|
$this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']);
|
||||||
|
|
||||||
$info['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
|
$info['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
|
||||||
$info['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
|
||||||
$info['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
|
$info['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
|
||||||
if (!empty($info['audio']['dataformat'])) {
|
if (!empty($info['audio']['dataformat'])) {
|
||||||
foreach ($info['audio'] as $key => $value) {
|
foreach ($info['audio'] as $key => $value) {
|
||||||
if ($key != 'streams') {
|
if ($key != 'streams') {
|
||||||
$info['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
|
$info['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'logical-fileinfo':
|
case 'logical-fileinfo':
|
||||||
// shortcut
|
// shortcut
|
||||||
$thisfile_real_chunks_currentchunk['logical_fileinfo'] = [];
|
$thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
|
$thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
|
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
$thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||||
|
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
$thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||||
|
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||||
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
|
||||||
|
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
|
||||||
|
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
||||||
//$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
//$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (empty($info['playtime_seconds'])) {
|
if (empty($info['playtime_seconds'])) {
|
||||||
$info['playtime_seconds'] = max($info['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
|
$info['playtime_seconds'] = max($info['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
|
||||||
}
|
}
|
||||||
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
|
||||||
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
switch ($thisfile_real_chunks_currentchunk['mime_type']) {
|
||||||
case 'audio/x-pn-realaudio':
|
case 'audio/x-pn-realaudio':
|
||||||
case 'audio/x-pn-multirate-realaudio':
|
case 'audio/x-pn-multirate-realaudio':
|
||||||
$info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
$info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||||
$info['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $info['audio']['bitrate']);
|
$info['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $info['audio']['bitrate']);
|
||||||
$info['audio']['dataformat'] = 'real';
|
$info['audio']['dataformat'] = 'real';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'video/x-pn-realvideo':
|
case 'video/x-pn-realvideo':
|
||||||
case 'video/x-pn-multirate-realvideo':
|
case 'video/x-pn-multirate-realvideo':
|
||||||
$info['video']['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
$info['video']['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||||
$info['video']['bitrate_mode'] = 'cbr';
|
$info['video']['bitrate_mode'] = 'cbr';
|
||||||
$info['video']['dataformat'] = 'real';
|
$info['video']['dataformat'] = 'real';
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'audio/x-ralf-mpeg4-generic':
|
case 'audio/x-ralf-mpeg4-generic':
|
||||||
$info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
$info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
|
||||||
$info['audio']['codec'] = 'RealAudio Lossless';
|
$info['audio']['codec'] = 'RealAudio Lossless';
|
||||||
$info['audio']['dataformat'] = 'real';
|
$info['audio']['dataformat'] = 'real';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$info['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
|
$info['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'CONT': // Content Description Header (text comments)
|
case 'CONT': // Content Description Header (text comments)
|
||||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||||
$thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
|
$thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['title_len'];
|
$offset += $thisfile_real_chunks_currentchunk['title_len'];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
|
$thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['artist_len'];
|
$offset += $thisfile_real_chunks_currentchunk['artist_len'];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
|
$thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['copyright_len'];
|
$offset += $thisfile_real_chunks_currentchunk['copyright_len'];
|
||||||
|
|
||||||
$thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
|
$thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
|
||||||
$offset += $thisfile_real_chunks_currentchunk['comment_len'];
|
$offset += $thisfile_real_chunks_currentchunk['comment_len'];
|
||||||
|
|
||||||
|
|
||||||
$commentkeystocopy = ['title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment'];
|
$commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment');
|
||||||
foreach ($commentkeystocopy as $key => $val) {
|
foreach ($commentkeystocopy as $key => $val) {
|
||||||
if ($thisfile_real_chunks_currentchunk[$key]) {
|
if ($thisfile_real_chunks_currentchunk[$key]) {
|
||||||
$info['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
|
$info['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 'DATA': // Data Chunk Header
|
case 'DATA': // Data Chunk Header
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'INDX': // Index Section Header
|
case 'INDX': // Index Section Header
|
||||||
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
|
||||||
$thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
$thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
$thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
|
if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
|
||||||
// last index chunk found, ignore rest of file
|
// last index chunk found, ignore rest of file
|
||||||
break 2;
|
break 2;
|
||||||
} else {
|
} else {
|
||||||
// non-last index chunk, seek to next index chunk (skipping actual index data)
|
// non-last index chunk, seek to next index chunk (skipping actual index data)
|
||||||
$this->fseek($thisfile_real_chunks_currentchunk['next_index_header']);
|
$this->fseek($thisfile_real_chunks_currentchunk['next_index_header']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$info['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset'];
|
$this->warning('Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$ChunkCounter++;
|
$ChunkCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($info['audio']['streams'])) {
|
if (!empty($info['audio']['streams'])) {
|
||||||
$info['audio']['bitrate'] = 0;
|
$info['audio']['bitrate'] = 0;
|
||||||
foreach ($info['audio']['streams'] as $key => $valuearray) {
|
foreach ($info['audio']['streams'] as $key => $valuearray) {
|
||||||
$info['audio']['bitrate'] += $valuearray['bitrate'];
|
$info['audio']['bitrate'] += $valuearray['bitrate'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function ParseOldRAheader($OldRAheaderData, &$ParsedArray)
|
public function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
|
||||||
{
|
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
||||||
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
|
|
||||||
|
|
||||||
$ParsedArray = [];
|
$ParsedArray = array();
|
||||||
$ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
|
$ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
|
||||||
if ($ParsedArray['magic'] != '.ra'."\xFD") {
|
if ($ParsedArray['magic'] != '.ra'."\xFD") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
|
$ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
|
||||||
|
|
||||||
if ($ParsedArray['version1'] < 3) {
|
if ($ParsedArray['version1'] < 3) {
|
||||||
return false;
|
|
||||||
} elseif ($ParsedArray['version1'] == 3) {
|
|
||||||
$ParsedArray['fourcc1'] = '.ra3';
|
|
||||||
$ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
|
|
||||||
$ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
|
|
||||||
|
|
||||||
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
return false;
|
||||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
|
|
||||||
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
|
|
||||||
//$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
|
|
||||||
//$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
|
|
||||||
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
|
||||||
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
|
||||||
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
|
|
||||||
|
|
||||||
$commentoffset = 0;
|
} elseif ($ParsedArray['version1'] == 3) {
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
|
||||||
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
|
||||||
$commentoffset += $commentlength;
|
|
||||||
|
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
$ParsedArray['fourcc1'] = '.ra3';
|
||||||
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
$ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
|
||||||
$commentoffset += $commentlength;
|
$ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
|
||||||
|
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
||||||
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
|
||||||
$commentoffset += $commentlength;
|
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
|
||||||
|
//$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
|
||||||
|
//$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
|
||||||
|
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
||||||
|
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
||||||
|
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
|
||||||
|
|
||||||
$commentoffset++; // final null terminator (?)
|
$commentoffset = 0;
|
||||||
$commentoffset++; // fourcc length (?) should be 4
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
$ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
|
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
} elseif ($ParsedArray['version1'] <= 5) {
|
$commentoffset += $commentlength;
|
||||||
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
|
||||||
$ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
|
|
||||||
$ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
|
|
||||||
$ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
|
||||||
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
|
||||||
$ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
|
|
||||||
$ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
|
|
||||||
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
|
|
||||||
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
|
|
||||||
//$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
|
|
||||||
$ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
|
|
||||||
$ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
|
|
||||||
$ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
|
|
||||||
//$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
|
|
||||||
|
|
||||||
switch ($ParsedArray['version1']) {
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
case 4:
|
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
|
$commentoffset += $commentlength;
|
||||||
//$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
|
|
||||||
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
|
|
||||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
|
|
||||||
$ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
|
|
||||||
$ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
|
|
||||||
$ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
|
|
||||||
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
|
|
||||||
//$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
|
|
||||||
//$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
|
|
||||||
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
|
|
||||||
|
|
||||||
$commentoffset = 0;
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
$commentoffset += $commentlength;
|
||||||
$commentoffset += $commentlength;
|
|
||||||
|
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
$commentoffset++; // final null terminator (?)
|
||||||
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
$commentoffset++; // fourcc length (?) should be 4
|
||||||
$commentoffset += $commentlength;
|
$ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
|
||||||
|
|
||||||
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
} elseif ($ParsedArray['version1'] <= 5) {
|
||||||
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
|
||||||
$commentoffset += $commentlength;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
//$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
|
||||||
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
|
$ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
|
||||||
$ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
|
$ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
|
||||||
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
|
$ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
|
||||||
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
|
$ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
|
||||||
$ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
|
$ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
|
||||||
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
|
$ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
|
||||||
$ParsedArray['comments'] = [];
|
$ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
|
||||||
break;
|
$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
|
||||||
}
|
//$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
|
||||||
$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
|
$ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
|
||||||
}
|
$ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
|
||||||
foreach ($ParsedArray['comments'] as $key => $value) {
|
$ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
|
||||||
if ($ParsedArray['comments'][$key][0] === false) {
|
//$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
|
||||||
$ParsedArray['comments'][$key][0] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
switch ($ParsedArray['version1']) {
|
||||||
}
|
|
||||||
|
|
||||||
public function RealAudioCodecFourCClookup($fourcc, $bitrate)
|
case 4:
|
||||||
{
|
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
|
||||||
static $RealAudioCodecFourCClookup = [];
|
//$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
|
||||||
if (empty($RealAudioCodecFourCClookup)) {
|
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
|
||||||
// http://www.its.msstate.edu/net/real/reports/config/tags.stats
|
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
|
||||||
// http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
|
$ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
|
||||||
|
$ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
|
||||||
|
$ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
|
||||||
|
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
|
||||||
|
//$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
|
||||||
|
//$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
|
||||||
|
$ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
|
||||||
|
|
||||||
$RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
|
$commentoffset = 0;
|
||||||
$RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
$RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
|
$ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
$RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
|
$commentoffset += $commentlength;
|
||||||
$RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
|
|
||||||
$RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
$RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
|
$ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
$RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
|
$commentoffset += $commentlength;
|
||||||
$RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
|
|
||||||
$RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
|
$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
|
||||||
$RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
|
$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
|
||||||
$RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
|
$commentoffset += $commentlength;
|
||||||
$RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
|
break;
|
||||||
$RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
|
|
||||||
$RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
|
case 5:
|
||||||
$RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
|
$ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
|
||||||
$RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
|
$ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
|
||||||
$RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
|
$ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
|
||||||
$RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
|
$ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
|
||||||
|
$ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
|
||||||
|
$ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
|
||||||
|
$ParsedArray['comments'] = array();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
|
||||||
|
|
||||||
|
}
|
||||||
|
foreach ($ParsedArray['comments'] as $key => $value) {
|
||||||
|
if ($ParsedArray['comments'][$key][0] === false) {
|
||||||
|
$ParsedArray['comments'][$key][0] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RealAudioCodecFourCClookup($fourcc, $bitrate) {
|
||||||
|
static $RealAudioCodecFourCClookup = array();
|
||||||
|
if (empty($RealAudioCodecFourCClookup)) {
|
||||||
|
// http://www.its.msstate.edu/net/real/reports/config/tags.stats
|
||||||
|
// http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
|
||||||
|
|
||||||
|
$RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||||
|
$RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||||
|
$RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
|
||||||
|
$RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
|
||||||
|
$RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
|
||||||
|
$RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
|
||||||
|
$RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
|
||||||
|
$RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
|
||||||
|
$RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
|
||||||
|
|
||||||
|
$RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
|
||||||
|
$RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
|
||||||
|
$RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
|
||||||
|
$RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
|
||||||
|
}
|
||||||
|
$roundbitrate = intval(round($bitrate));
|
||||||
|
if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
|
||||||
|
return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
|
||||||
|
} elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
|
||||||
|
return $RealAudioCodecFourCClookup[$fourcc][0];
|
||||||
|
}
|
||||||
|
return $fourcc;
|
||||||
|
}
|
||||||
|
|
||||||
$RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
|
|
||||||
$RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
|
|
||||||
$RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
|
|
||||||
$RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
|
|
||||||
}
|
|
||||||
$roundbitrate = intval(round($bitrate));
|
|
||||||
if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
|
|
||||||
return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
|
|
||||||
} elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
|
|
||||||
return $RealAudioCodecFourCClookup[$fourcc][0];
|
|
||||||
}
|
|
||||||
return $fourcc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,124 +17,124 @@
|
||||||
|
|
||||||
class getid3_swf extends getid3_handler
|
class getid3_swf extends getid3_handler
|
||||||
{
|
{
|
||||||
public $ReturnAllTagData = false;
|
public $ReturnAllTagData = false;
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'swf';
|
$info['fileformat'] = 'swf';
|
||||||
$info['video']['dataformat'] = 'swf';
|
$info['video']['dataformat'] = 'swf';
|
||||||
|
|
||||||
// http://www.openswf.org/spec/SWFfileformat.html
|
// http://www.openswf.org/spec/SWFfileformat.html
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
$SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
|
$SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
|
||||||
|
|
||||||
$info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
|
$info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
|
||||||
switch ($info['swf']['header']['signature']) {
|
switch ($info['swf']['header']['signature']) {
|
||||||
case 'FWS':
|
case 'FWS':
|
||||||
$info['swf']['header']['compressed'] = false;
|
$info['swf']['header']['compressed'] = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'CWS':
|
case 'CWS':
|
||||||
$info['swf']['header']['compressed'] = true;
|
$info['swf']['header']['compressed'] = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$info['error'][] = 'Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"';
|
$this->error('Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"');
|
||||||
unset($info['swf']);
|
unset($info['swf']);
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
|
$info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
|
||||||
$info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
|
$info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
|
||||||
|
|
||||||
if ($info['swf']['header']['compressed']) {
|
if ($info['swf']['header']['compressed']) {
|
||||||
$SWFHead = substr($SWFfileData, 0, 8);
|
$SWFHead = substr($SWFfileData, 0, 8);
|
||||||
$SWFfileData = substr($SWFfileData, 8);
|
$SWFfileData = substr($SWFfileData, 8);
|
||||||
if ($decompressed = @gzuncompress($SWFfileData)) {
|
if ($decompressed = @gzuncompress($SWFfileData)) {
|
||||||
$SWFfileData = $SWFHead.$decompressed;
|
$SWFfileData = $SWFHead.$decompressed;
|
||||||
} else {
|
} else {
|
||||||
$info['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)';
|
$this->error('Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
|
$FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
|
||||||
$FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
|
$FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
|
||||||
$FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 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++) {
|
for ($i = 1; $i < $FrameSizeDataLength; $i++) {
|
||||||
$FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
|
$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));
|
list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
|
||||||
$info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
|
$info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
|
||||||
$info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
|
$info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
|
||||||
|
|
||||||
// http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
|
// http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
|
||||||
// Next in the header is the frame rate, which is kind of weird.
|
// 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
|
// 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.
|
// (or last depending on how you look at it) is completely ignored.
|
||||||
// Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
|
// Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
|
||||||
|
|
||||||
// Byte at (8 + $FrameSizeDataLength) is always zero and ignored
|
// Byte at (8 + $FrameSizeDataLength) is always zero and ignored
|
||||||
$info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
|
$info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
|
||||||
$info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
|
$info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
|
||||||
|
|
||||||
$info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
|
$info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
|
||||||
$info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
|
$info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
|
||||||
$info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
|
$info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
|
|
||||||
if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) {
|
if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) {
|
||||||
$info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
|
$info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
|
||||||
}
|
}
|
||||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||||
|
|
||||||
|
|
||||||
// SWF tags
|
// SWF tags
|
||||||
|
|
||||||
$CurrentOffset = 12 + $FrameSizeDataLength;
|
$CurrentOffset = 12 + $FrameSizeDataLength;
|
||||||
$SWFdataLength = strlen($SWFfileData);
|
$SWFdataLength = strlen($SWFfileData);
|
||||||
|
|
||||||
while ($CurrentOffset < $SWFdataLength) {
|
while ($CurrentOffset < $SWFdataLength) {
|
||||||
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
|
||||||
|
|
||||||
$TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
|
$TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
|
||||||
$TagID = ($TagIDTagLength & 0xFFFC) >> 6;
|
$TagID = ($TagIDTagLength & 0xFFFC) >> 6;
|
||||||
$TagLength = ($TagIDTagLength & 0x003F);
|
$TagLength = ($TagIDTagLength & 0x003F);
|
||||||
$CurrentOffset += 2;
|
$CurrentOffset += 2;
|
||||||
if ($TagLength == 0x3F) {
|
if ($TagLength == 0x3F) {
|
||||||
$TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
|
$TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
|
||||||
$CurrentOffset += 4;
|
$CurrentOffset += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($TagData);
|
unset($TagData);
|
||||||
$TagData['offset'] = $CurrentOffset;
|
$TagData['offset'] = $CurrentOffset;
|
||||||
$TagData['size'] = $TagLength;
|
$TagData['size'] = $TagLength;
|
||||||
$TagData['id'] = $TagID;
|
$TagData['id'] = $TagID;
|
||||||
$TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
|
$TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
|
||||||
switch ($TagID) {
|
switch ($TagID) {
|
||||||
case 0: // end of movie
|
case 0: // end of movie
|
||||||
break 2;
|
break 2;
|
||||||
|
|
||||||
case 9: // Set background color
|
case 9: // Set background color
|
||||||
//$info['swf']['tags'][] = $TagData;
|
//$info['swf']['tags'][] = $TagData;
|
||||||
$info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
|
$info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ($this->ReturnAllTagData) {
|
if ($this->ReturnAllTagData) {
|
||||||
$info['swf']['tags'][] = $TagData;
|
$info['swf']['tags'][] = $TagData;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$CurrentOffset += $TagLength;
|
$CurrentOffset += $TagLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,69 +18,62 @@
|
||||||
class getid3_ts extends getid3_handler
|
class getid3_ts extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$TSheader = $this->fread(19);
|
$TSheader = $this->fread(19);
|
||||||
$magic = "\x47";
|
$magic = "\x47";
|
||||||
if (substr($TSheader, 0, 1) != $magic) {
|
if (substr($TSheader, 0, 1) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($TSheader, 0, 1)).' instead.';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($TSheader, 0, 1)).' instead.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'ts';
|
$info['fileformat'] = 'ts';
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/.ts
|
// http://en.wikipedia.org/wiki/.ts
|
||||||
|
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$info['ts']['packet']['sync'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1));
|
$info['ts']['packet']['sync'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1;
|
||||||
$offset += 1;
|
$pid_flags_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2;
|
||||||
$pid_flags_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2));
|
$SAC_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1;
|
||||||
$offset += 2;
|
$info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000); // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
|
||||||
$SAC_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1));
|
$info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000); // 1 means start of PES data or PSI otherwise zero only.
|
||||||
$offset += 1;
|
$info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000); // 1 means higher priority than other packets with the same PID.
|
||||||
$info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000); // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
|
$info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1FFF) >> 0;
|
||||||
$info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000); // 1 means start of PES data or PSI otherwise zero only.
|
|
||||||
$info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000); // 1 means higher priority than other packets with the same PID.
|
|
||||||
$info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1FFF) >> 0;
|
|
||||||
|
|
||||||
$info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xC0) >> 6;
|
$info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xC0) >> 6;
|
||||||
$info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20);
|
$info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20);
|
||||||
$info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10);
|
$info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10);
|
||||||
$info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0x0F) >> 0; // Incremented only when a payload is present
|
$info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0x0F) >> 0; // Incremented only when a payload is present
|
||||||
$info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']);
|
$info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']);
|
||||||
|
|
||||||
if ($info['ts']['packet']['flags']['adaption_field_exists']) {
|
if ($info['ts']['packet']['flags']['adaption_field_exists']) {
|
||||||
$AdaptionField_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2));
|
$AdaptionField_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2;
|
||||||
$offset += 2;
|
$info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xFF00) >> 8; // Number of bytes in the adaptation field immediately following this byte
|
||||||
$info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xFF00) >> 8; // Number of bytes in the adaptation field immediately following this byte
|
$info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x0080); // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference
|
||||||
$info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x0080); // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference
|
$info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x0040); // Set to 1 if the PES packet in this TS packet starts a video/audio sequence
|
||||||
$info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x0040); // Set to 1 if the PES packet in this TS packet starts a video/audio sequence
|
$info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x0020); // 1 = higher priority
|
||||||
$info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x0020); // 1 = higher priority
|
$info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x0010); // 1 means adaptation field does contain a PCR field
|
||||||
$info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x0010); // 1 means adaptation field does contain a PCR field
|
$info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x0008); // 1 means adaptation field does contain an OPCR field
|
||||||
$info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x0008); // 1 means adaptation field does contain an OPCR field
|
$info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x0004); // 1 means presence of splice countdown field in adaptation field
|
||||||
$info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x0004); // 1 means presence of splice countdown field in adaptation field
|
$info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x0002); // 1 means presence of private data bytes in adaptation field
|
||||||
$info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x0002); // 1 means presence of private data bytes in adaptation field
|
$info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x0001); // 1 means presence of adaptation field extension
|
||||||
$info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x0001); // 1 means presence of adaptation field extension
|
if ($info['ts']['packet']['adaption']['flags']['pcr']) {
|
||||||
if ($info['ts']['packet']['adaption']['flags']['pcr']) {
|
$info['ts']['packet']['adaption']['raw']['pcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6;
|
||||||
$info['ts']['packet']['adaption']['raw']['pcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6));
|
}
|
||||||
$offset += 6;
|
if ($info['ts']['packet']['adaption']['flags']['opcr']) {
|
||||||
}
|
$info['ts']['packet']['adaption']['raw']['opcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6;
|
||||||
if ($info['ts']['packet']['adaption']['flags']['opcr']) {
|
}
|
||||||
$info['ts']['packet']['adaption']['raw']['opcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6));
|
}
|
||||||
$offset += 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['error'][] = 'MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function TSscramblingControlLookup($raw)
|
public function TSscramblingControlLookup($raw) {
|
||||||
{
|
$TSscramblingControlLookup = array(0x00=>'not scrambled', 0x01=>'reserved', 0x02=>'scrambled, even key', 0x03=>'scrambled, odd key');
|
||||||
$TSscramblingControlLookup = [0x00=>'not scrambled', 0x01=>'reserved', 0x02=>'scrambled, even key', 0x03=>'scrambled, odd key'];
|
return (isset($TSscramblingControlLookup[$raw]) ? $TSscramblingControlLookup[$raw] : 'invalid');
|
||||||
return (isset($TSscramblingControlLookup[$raw]) ? $TSscramblingControlLookup[$raw] : 'invalid');
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,42 +18,42 @@
|
||||||
class getid3_aa extends getid3_handler
|
class getid3_aa extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$AAheader = $this->fread(8);
|
$AAheader = $this->fread(8);
|
||||||
|
|
||||||
$magic = "\x57\x90\x75\x36";
|
$magic = "\x57\x90\x75\x36";
|
||||||
if (substr($AAheader, 4, 4) != $magic) {
|
if (substr($AAheader, 4, 4) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AAheader, 4, 4)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AAheader, 4, 4)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['aa'] = [];
|
$info['aa'] = array();
|
||||||
$thisfile_aa = &$info['aa'];
|
$thisfile_aa = &$info['aa'];
|
||||||
|
|
||||||
$info['fileformat'] = 'aa';
|
$info['fileformat'] = 'aa';
|
||||||
$info['audio']['dataformat'] = 'aa';
|
$info['audio']['dataformat'] = 'aa';
|
||||||
$info['error'][] = 'Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
$info['audio']['bitrate_mode'] = 'cbr'; // is it?
|
$info['audio']['bitrate_mode'] = 'cbr'; // is it?
|
||||||
$thisfile_aa['encoding'] = 'ISO-8859-1';
|
$thisfile_aa['encoding'] = 'ISO-8859-1';
|
||||||
|
|
||||||
$thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4));
|
$thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4));
|
||||||
if ($thisfile_aa['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) {
|
if ($thisfile_aa['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) {
|
||||||
$info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_aa['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"';
|
$this->warning('Possible truncated file - expecting "'.$thisfile_aa['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"');
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['audio']['bits_per_sample'] = 16; // is it?
|
$info['audio']['bits_per_sample'] = 16; // is it?
|
||||||
$info['audio']['sample_rate'] = $thisfile_aa['sample_rate'];
|
$info['audio']['sample_rate'] = $thisfile_aa['sample_rate'];
|
||||||
$info['audio']['channels'] = $thisfile_aa['channels'];
|
$info['audio']['channels'] = $thisfile_aa['channels'];
|
||||||
|
|
||||||
//$info['playtime_seconds'] = 0;
|
//$info['playtime_seconds'] = 0;
|
||||||
//$info['audio']['bitrate'] = 0;
|
//$info['audio']['bitrate'] = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,396 +17,398 @@
|
||||||
|
|
||||||
class getid3_aac extends getid3_handler
|
class getid3_aac extends getid3_handler
|
||||||
{
|
{
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
if ($this->fread(4) == 'ADIF') {
|
||||||
if ($this->fread(4) == 'ADIF') {
|
$this->getAACADIFheaderFilepointer();
|
||||||
$this->getAACADIFheaderFilepointer();
|
} else {
|
||||||
} else {
|
$this->getAACADTSheaderFilepointer();
|
||||||
$this->getAACADTSheaderFilepointer();
|
}
|
||||||
}
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function getAACADIFheaderFilepointer()
|
public function getAACADIFheaderFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$info['fileformat'] = 'aac';
|
||||||
$info['fileformat'] = 'aac';
|
$info['audio']['dataformat'] = 'aac';
|
||||||
$info['audio']['dataformat'] = 'aac';
|
$info['audio']['lossless'] = false;
|
||||||
$info['audio']['lossless'] = false;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$AACheader = $this->fread(1024);
|
$AACheader = $this->fread(1024);
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
|
||||||
if (substr($AACheader, 0, 4) == 'ADIF') {
|
if (substr($AACheader, 0, 4) == 'ADIF') {
|
||||||
// http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
|
||||||
|
|
||||||
// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
// http://faac.sourceforge.net/wiki/index.php?page=ADIF
|
||||||
// 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);
|
// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
|
||||||
$bitoffset = 0;
|
// 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()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
$info['aac']['header_type'] = 'ADIF';
|
$AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
|
||||||
$bitoffset += 32;
|
$bitoffset = 0;
|
||||||
$info['aac']['header']['mpeg_version'] = 4;
|
|
||||||
|
|
||||||
$info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
$info['aac']['header_type'] = 'ADIF';
|
||||||
$bitoffset += 1;
|
$bitoffset += 32;
|
||||||
if ($info['aac']['header']['copyright']) {
|
$info['aac']['header']['mpeg_version'] = 4;
|
||||||
$info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
|
|
||||||
$bitoffset += 72;
|
|
||||||
}
|
|
||||||
$info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
|
||||||
$bitoffset += 1;
|
|
||||||
if ($info['aac']['header']['is_vbr']) {
|
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
|
||||||
$info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
|
||||||
$bitoffset += 23;
|
|
||||||
} else {
|
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
|
||||||
$info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
|
||||||
$bitoffset += 23;
|
|
||||||
$info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
|
|
||||||
}
|
|
||||||
if ($info['audio']['bitrate'] == 0) {
|
|
||||||
$info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
|
|
||||||
for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
|
$info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||||
// http://www.audiocoding.com/wiki/index.php?page=program_config_element
|
$bitoffset += 1;
|
||||||
|
if ($info['aac']['header']['copyright']) {
|
||||||
|
$info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
|
||||||
|
$bitoffset += 72;
|
||||||
|
}
|
||||||
|
$info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
|
||||||
|
$bitoffset += 1;
|
||||||
|
if ($info['aac']['header']['is_vbr']) {
|
||||||
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
$info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
||||||
|
$bitoffset += 23;
|
||||||
|
} else {
|
||||||
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
|
$info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
|
||||||
|
$bitoffset += 23;
|
||||||
|
$info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
|
||||||
|
}
|
||||||
|
if ($info['audio']['bitrate'] == 0) {
|
||||||
|
$this->error('Corrupt AAC file: bitrate_audio == zero');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
|
||||||
// buffer_fullness 20
|
for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
|
||||||
|
// http://www.audiocoding.com/wiki/index.php?page=program_config_element
|
||||||
|
|
||||||
// element_instance_tag 4
|
// buffer_fullness 20
|
||||||
// object_type 2
|
|
||||||
// sampling_frequency_index 4
|
|
||||||
// num_front_channel_elements 4
|
|
||||||
// num_side_channel_elements 4
|
|
||||||
// num_back_channel_elements 4
|
|
||||||
// num_lfe_channel_elements 2
|
|
||||||
// num_assoc_data_elements 3
|
|
||||||
// num_valid_cc_elements 4
|
|
||||||
// mono_mixdown_present 1
|
|
||||||
// mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
|
||||||
// stereo_mixdown_present 1
|
|
||||||
// stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
|
||||||
// matrix_mixdown_idx_present 1
|
|
||||||
// matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
|
||||||
// pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
|
||||||
// for (i = 0; i < num_front_channel_elements; i++) {
|
|
||||||
// front_element_is_cpe[i] 1
|
|
||||||
// front_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// for (i = 0; i < num_side_channel_elements; i++) {
|
|
||||||
// side_element_is_cpe[i] 1
|
|
||||||
// side_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// for (i = 0; i < num_back_channel_elements; i++) {
|
|
||||||
// back_element_is_cpe[i] 1
|
|
||||||
// back_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// for (i = 0; i < num_lfe_channel_elements; i++) {
|
|
||||||
// lfe_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// for (i = 0; i < num_assoc_data_elements; i++) {
|
|
||||||
// assoc_data_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// for (i = 0; i < num_valid_cc_elements; i++) {
|
|
||||||
// cc_element_is_ind_sw[i] 1
|
|
||||||
// valid_cc_element_tag_select[i] 4
|
|
||||||
// }
|
|
||||||
// byte_alignment() VAR
|
|
||||||
// comment_field_bytes 8
|
|
||||||
// for (i = 0; i < comment_field_bytes; i++) {
|
|
||||||
// comment_field_data[i] 8
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (!$info['aac']['header']['is_vbr']) {
|
// element_instance_tag 4
|
||||||
$info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
|
// object_type 2
|
||||||
$bitoffset += 20;
|
// sampling_frequency_index 4
|
||||||
}
|
// num_front_channel_elements 4
|
||||||
$info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// num_side_channel_elements 4
|
||||||
$bitoffset += 4;
|
// num_back_channel_elements 4
|
||||||
$info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
// num_lfe_channel_elements 2
|
||||||
$bitoffset += 2;
|
// num_assoc_data_elements 3
|
||||||
$info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// num_valid_cc_elements 4
|
||||||
$bitoffset += 4;
|
// mono_mixdown_present 1
|
||||||
$info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// mono_mixdown_element_number 4 if mono_mixdown_present == 1
|
||||||
$bitoffset += 4;
|
// stereo_mixdown_present 1
|
||||||
$info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// stereo_mixdown_element_number 4 if stereo_mixdown_present == 1
|
||||||
$bitoffset += 4;
|
// matrix_mixdown_idx_present 1
|
||||||
$info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1
|
||||||
$bitoffset += 4;
|
// pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1
|
||||||
$info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
// for (i = 0; i < num_front_channel_elements; i++) {
|
||||||
$bitoffset += 2;
|
// front_element_is_cpe[i] 1
|
||||||
$info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
// front_element_tag_select[i] 4
|
||||||
$bitoffset += 3;
|
// }
|
||||||
$info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// for (i = 0; i < num_side_channel_elements; i++) {
|
||||||
$bitoffset += 4;
|
// side_element_is_cpe[i] 1
|
||||||
$info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
// side_element_tag_select[i] 4
|
||||||
$bitoffset += 1;
|
// }
|
||||||
if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
|
// for (i = 0; i < num_back_channel_elements; i++) {
|
||||||
$info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// back_element_is_cpe[i] 1
|
||||||
$bitoffset += 4;
|
// back_element_tag_select[i] 4
|
||||||
}
|
// }
|
||||||
$info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
// for (i = 0; i < num_lfe_channel_elements; i++) {
|
||||||
$bitoffset += 1;
|
// lfe_element_tag_select[i] 4
|
||||||
if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
|
// }
|
||||||
$info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
// for (i = 0; i < num_assoc_data_elements; i++) {
|
||||||
$bitoffset += 4;
|
// assoc_data_element_tag_select[i] 4
|
||||||
}
|
// }
|
||||||
$info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
// for (i = 0; i < num_valid_cc_elements; i++) {
|
||||||
$bitoffset += 1;
|
// cc_element_is_ind_sw[i] 1
|
||||||
if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
|
// valid_cc_element_tag_select[i] 4
|
||||||
$info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
// }
|
||||||
$bitoffset += 2;
|
// byte_alignment() VAR
|
||||||
$info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
// comment_field_bytes 8
|
||||||
$bitoffset += 1;
|
// for (i = 0; i < comment_field_bytes; i++) {
|
||||||
}
|
// comment_field_data[i] 8
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
|
// }
|
||||||
$info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
|
|
||||||
$info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
|
|
||||||
$info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
|
|
||||||
$info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
|
|
||||||
$info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
|
|
||||||
$info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
|
||||||
$bitoffset += 1;
|
|
||||||
$info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
|
||||||
$bitoffset += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
$bitoffset = ceil($bitoffset / 8) * 8;
|
if (!$info['aac']['header']['is_vbr']) {
|
||||||
|
$info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
|
||||||
|
$bitoffset += 20;
|
||||||
|
}
|
||||||
|
$info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||||
|
$bitoffset += 2;
|
||||||
|
$info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||||
|
$bitoffset += 2;
|
||||||
|
$info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
|
||||||
|
$bitoffset += 3;
|
||||||
|
$info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
$info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
|
||||||
|
$info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
$info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
|
||||||
|
$info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
$info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
|
||||||
|
$info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
|
||||||
|
$bitoffset += 2;
|
||||||
|
$info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
|
||||||
|
$info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
|
||||||
|
$bitoffset += 1;
|
||||||
|
$info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
|
||||||
|
$bitoffset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
$info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
|
$bitoffset = ceil($bitoffset / 8) * 8;
|
||||||
$bitoffset += 8;
|
|
||||||
$info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
|
$info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
|
||||||
$bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
|
$bitoffset += 8;
|
||||||
|
$info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
|
||||||
|
$bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
|
||||||
|
|
||||||
|
|
||||||
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
|
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
|
||||||
$info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
|
$info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
|
||||||
$info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
|
$info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
|
||||||
$info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
|
$info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
|
||||||
if ($info['aac']['program_configs'][$i]['comment_field']) {
|
if ($info['aac']['program_configs'][$i]['comment_field']) {
|
||||||
$info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
|
$info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
||||||
|
|
||||||
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
unset($info['fileformat']);
|
} else {
|
||||||
unset($info['aac']);
|
|
||||||
$info['error'][] = 'AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
|
unset($info['fileformat']);
|
||||||
return false;
|
unset($info['aac']);
|
||||||
}
|
$this->error('AAC-ADIF synch not found at offset '.$info['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)');
|
||||||
}
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
|
public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de>
|
// 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://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||||
|
|
||||||
|
|
||||||
// http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
|
// http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
|
||||||
// http://wiki.multimedia.cx/index.php?title=ADTS
|
// http://wiki.multimedia.cx/index.php?title=ADTS
|
||||||
|
|
||||||
// * ADTS Fixed Header: these don't change from frame to frame
|
// * ADTS Fixed Header: these don't change from frame to frame
|
||||||
// syncword 12 always: '111111111111'
|
// syncword 12 always: '111111111111'
|
||||||
// ID 1 0: MPEG-4, 1: MPEG-2
|
// ID 1 0: MPEG-4, 1: MPEG-2
|
||||||
// MPEG layer 2 If you send AAC in MPEG-TS, set to 0
|
// MPEG layer 2 If you send AAC in MPEG-TS, set to 0
|
||||||
// protection_absent 1 0: CRC present; 1: no CRC
|
// protection_absent 1 0: CRC present; 1: no CRC
|
||||||
// profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
|
// profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
|
||||||
// sampling_frequency_index 4 15 not allowed
|
// sampling_frequency_index 4 15 not allowed
|
||||||
// private_bit 1 usually 0
|
// private_bit 1 usually 0
|
||||||
// channel_configuration 3
|
// channel_configuration 3
|
||||||
// original/copy 1 0: original; 1: copy
|
// original/copy 1 0: original; 1: copy
|
||||||
// home 1 usually 0
|
// home 1 usually 0
|
||||||
// emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation?
|
// emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation?
|
||||||
|
|
||||||
// * ADTS Variable Header: these can change from frame to frame
|
// * ADTS Variable Header: these can change from frame to frame
|
||||||
// copyright_identification_bit 1
|
// copyright_identification_bit 1
|
||||||
// copyright_identification_start 1
|
// copyright_identification_start 1
|
||||||
// aac_frame_length 13 length of the frame including header (in bytes)
|
// aac_frame_length 13 length of the frame including header (in bytes)
|
||||||
// adts_buffer_fullness 11 0x7FF indicates VBR
|
// adts_buffer_fullness 11 0x7FF indicates VBR
|
||||||
// no_raw_data_blocks_in_frame 2
|
// no_raw_data_blocks_in_frame 2
|
||||||
|
|
||||||
// * ADTS Error check
|
// * ADTS Error check
|
||||||
// crc_check 16 only if protection_absent == 0
|
// crc_check 16 only if protection_absent == 0
|
||||||
|
|
||||||
$byteoffset = $info['avdataoffset'];
|
$byteoffset = $info['avdataoffset'];
|
||||||
$framenumber = 0;
|
$framenumber = 0;
|
||||||
|
|
||||||
// Init bit pattern array
|
// Init bit pattern array
|
||||||
static $decbin = [];
|
static $decbin = array();
|
||||||
|
|
||||||
// Populate $bindec
|
// Populate $bindec
|
||||||
for ($i = 0; $i < 256; $i++) {
|
for ($i = 0; $i < 256; $i++) {
|
||||||
$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
|
$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to calculate bitrate below
|
// used to calculate bitrate below
|
||||||
$BitrateCache = [];
|
$BitrateCache = array();
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// breaks out when end-of-file encountered, or invalid data found,
|
// breaks out when end-of-file encountered, or invalid data found,
|
||||||
// or MaxFramesToScan frames have been scanned
|
// or MaxFramesToScan frames have been scanned
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($byteoffset)) {
|
if (!getid3_lib::intValueSupported($byteoffset)) {
|
||||||
$info['warning'][] = 'Unable to parse AAC file beyond '.$this->ftell().' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
|
$this->warning('Unable to parse AAC file beyond '.$this->ftell().' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$this->fseek($byteoffset);
|
$this->fseek($byteoffset);
|
||||||
|
|
||||||
// First get substring
|
// First get substring
|
||||||
$substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present)
|
$substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present)
|
||||||
$substringlength = strlen($substring);
|
$substringlength = strlen($substring);
|
||||||
if ($substringlength != 9) {
|
if ($substringlength != 9) {
|
||||||
$info['error'][] = 'Failed to read 7 bytes at offset '.($this->ftell() - $substringlength).' (only read '.$substringlength.' bytes)';
|
$this->error('Failed to read 7 bytes at offset '.($this->ftell() - $substringlength).' (only read '.$substringlength.' bytes)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// this would be easier with 64-bit math, but split it up to allow for 32-bit:
|
// this would be easier with 64-bit math, but split it up to allow for 32-bit:
|
||||||
$header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
|
$header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
|
||||||
$header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
|
$header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
|
||||||
$header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
|
$header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
|
||||||
|
|
||||||
$info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
|
$info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
|
||||||
if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
|
if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
|
||||||
$info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.($this->ftell() - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)';
|
$this->error('Synch pattern (0x0FFF) not found at offset '.($this->ftell() - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)');
|
||||||
//if ($info['fileformat'] == 'aac') {
|
//if ($info['fileformat'] == 'aac') {
|
||||||
// return true;
|
// return true;
|
||||||
//}
|
//}
|
||||||
unset($info['aac']);
|
unset($info['aac']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather info for first frame only - this takes time to do 1000 times!
|
// Gather info for first frame only - this takes time to do 1000 times!
|
||||||
if ($framenumber == 0) {
|
if ($framenumber == 0) {
|
||||||
$info['aac']['header_type'] = 'ADTS';
|
$info['aac']['header_type'] = 'ADTS';
|
||||||
$info['fileformat'] = 'aac';
|
$info['fileformat'] = 'aac';
|
||||||
$info['audio']['dataformat'] = 'aac';
|
$info['audio']['dataformat'] = 'aac';
|
||||||
|
|
||||||
$info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
|
$info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x0008) >> 3;
|
||||||
$info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
|
$info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x0006) >> 1;
|
||||||
$info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
|
$info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x0001) >> 0;
|
||||||
|
|
||||||
$info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
|
$info['aac']['header']['raw']['profile_code'] = ($header2 & 0xC0000000) >> 30;
|
||||||
$info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
|
$info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3C000000) >> 26;
|
||||||
$info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
|
$info['aac']['header']['raw']['private_stream'] = ($header2 & 0x02000000) >> 25;
|
||||||
$info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
|
$info['aac']['header']['raw']['channels_code'] = ($header2 & 0x01C00000) >> 22;
|
||||||
$info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
|
$info['aac']['header']['raw']['original'] = ($header2 & 0x00200000) >> 21;
|
||||||
$info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
|
$info['aac']['header']['raw']['home'] = ($header2 & 0x00100000) >> 20;
|
||||||
$info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
|
$info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x00080000) >> 19;
|
||||||
$info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
|
$info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x00040000) >> 18;
|
||||||
$info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
|
$info['aac']['header']['raw']['frame_length'] = ($header2 & 0x0003FFE0) >> 5;
|
||||||
|
|
||||||
$info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
|
$info['aac']['header']['mpeg_version'] = ($info['aac']['header']['raw']['mpeg_version'] ? 2 : 4);
|
||||||
$info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
|
$info['aac']['header']['crc_present'] = ($info['aac']['header']['raw']['protection_absent'] ? false: true);
|
||||||
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
|
$info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
|
||||||
$info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
|
$info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
|
||||||
$info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
|
$info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
|
||||||
$info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
|
$info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
|
||||||
$info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
|
$info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
|
||||||
$info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
|
$info['aac']['header']['channels'] = (($info['aac']['header']['raw']['channels_code'] == 7) ? 8 : $info['aac']['header']['raw']['channels_code']);
|
||||||
if ($ReturnExtendedInfo) {
|
if ($ReturnExtendedInfo) {
|
||||||
$info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
|
$info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
|
||||||
$info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
|
$info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
|
if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
|
||||||
$info['warning'][] = 'Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead';
|
$this->warning('Layer error - expected "0", found "'.$info['aac']['header']['raw']['mpeg_layer'].'" instead');
|
||||||
}
|
}
|
||||||
if ($info['aac']['header']['sample_frequency'] == 0) {
|
if ($info['aac']['header']['sample_frequency'] == 0) {
|
||||||
$info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
|
$this->error('Corrupt AAC file: sample_frequency == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
|
$info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
|
||||||
$info['audio']['channels'] = $info['aac']['header']['channels'];
|
$info['audio']['channels'] = $info['aac']['header']['channels'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$FrameLength = ($header2 & 0x0003FFE0) >> 5;
|
$FrameLength = ($header2 & 0x0003FFE0) >> 5;
|
||||||
|
|
||||||
if (!isset($BitrateCache[$FrameLength])) {
|
if (!isset($BitrateCache[$FrameLength])) {
|
||||||
$BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
|
$BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
|
||||||
}
|
}
|
||||||
getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
|
getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
|
||||||
|
|
||||||
$info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
|
$info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
|
||||||
|
|
||||||
$info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
|
$info['aac'][$framenumber]['adts_buffer_fullness'] = (($header2 & 0x0000001F) << 6) & (($header3 & 0xFC) >> 2);
|
||||||
if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
|
if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
} else {
|
} else {
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
}
|
}
|
||||||
$info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
|
$info['aac'][$framenumber]['num_raw_data_blocks'] = (($header3 & 0x03) >> 0);
|
||||||
|
|
||||||
if ($info['aac']['header']['crc_present']) {
|
if ($info['aac']['header']['crc_present']) {
|
||||||
//$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
|
//$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$ReturnExtendedInfo) {
|
if (!$ReturnExtendedInfo) {
|
||||||
unset($info['aac'][$framenumber]);
|
unset($info['aac'][$framenumber]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
$rounded_precision = 5000;
|
$rounded_precision = 5000;
|
||||||
$info['aac']['bitrate_distribution_rounded'] = array();
|
$info['aac']['bitrate_distribution_rounded'] = array();
|
||||||
foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
|
foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
|
||||||
|
@ -416,94 +418,96 @@ class getid3_aac extends getid3_handler
|
||||||
ksort($info['aac']['bitrate_distribution_rounded']);
|
ksort($info['aac']['bitrate_distribution_rounded']);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$byteoffset += $FrameLength;
|
$byteoffset += $FrameLength;
|
||||||
if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) {
|
if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $info['avdataend'])) {
|
||||||
// keep scanning
|
|
||||||
} else {
|
|
||||||
$info['aac']['frames'] = $framenumber;
|
|
||||||
$info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
|
|
||||||
if ($info['playtime_seconds'] == 0) {
|
|
||||||
$info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
|
||||||
ksort($info['aac']['bitrate_distribution']);
|
|
||||||
|
|
||||||
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
// keep scanning
|
||||||
|
|
||||||
return true;
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
// should never get here.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function AACsampleRateLookup($samplerateid)
|
$info['aac']['frames'] = $framenumber;
|
||||||
{
|
$info['playtime_seconds'] = ($info['avdataend'] / $byteoffset) * (($framenumber * 1024) / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
|
||||||
static $AACsampleRateLookup = [];
|
if ($info['playtime_seconds'] == 0) {
|
||||||
if (empty($AACsampleRateLookup)) {
|
$this->error('Corrupt AAC file: playtime_seconds == zero');
|
||||||
$AACsampleRateLookup[0] = 96000;
|
return false;
|
||||||
$AACsampleRateLookup[1] = 88200;
|
}
|
||||||
$AACsampleRateLookup[2] = 64000;
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
$AACsampleRateLookup[3] = 48000;
|
ksort($info['aac']['bitrate_distribution']);
|
||||||
$AACsampleRateLookup[4] = 44100;
|
|
||||||
$AACsampleRateLookup[5] = 32000;
|
|
||||||
$AACsampleRateLookup[6] = 24000;
|
|
||||||
$AACsampleRateLookup[7] = 22050;
|
|
||||||
$AACsampleRateLookup[8] = 16000;
|
|
||||||
$AACsampleRateLookup[9] = 12000;
|
|
||||||
$AACsampleRateLookup[10] = 11025;
|
|
||||||
$AACsampleRateLookup[11] = 8000;
|
|
||||||
$AACsampleRateLookup[12] = 0;
|
|
||||||
$AACsampleRateLookup[13] = 0;
|
|
||||||
$AACsampleRateLookup[14] = 0;
|
|
||||||
$AACsampleRateLookup[15] = 0;
|
|
||||||
}
|
|
||||||
return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function AACprofileLookup($profileid, $mpegversion)
|
$info['audio']['encoder_options'] = $info['aac']['header_type'].' '.$info['aac']['header']['profile'];
|
||||||
{
|
|
||||||
static $AACprofileLookup = [];
|
return true;
|
||||||
if (empty($AACprofileLookup)) {
|
|
||||||
$AACprofileLookup[2][0] = 'Main profile';
|
}
|
||||||
$AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
|
}
|
||||||
$AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
|
// should never get here.
|
||||||
$AACprofileLookup[2][3] = '(reserved)';
|
}
|
||||||
$AACprofileLookup[4][0] = 'AAC_MAIN';
|
|
||||||
$AACprofileLookup[4][1] = 'AAC_LC';
|
public static function AACsampleRateLookup($samplerateid) {
|
||||||
$AACprofileLookup[4][2] = 'AAC_SSR';
|
static $AACsampleRateLookup = array();
|
||||||
$AACprofileLookup[4][3] = 'AAC_LTP';
|
if (empty($AACsampleRateLookup)) {
|
||||||
}
|
$AACsampleRateLookup[0] = 96000;
|
||||||
return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
|
$AACsampleRateLookup[1] = 88200;
|
||||||
}
|
$AACsampleRateLookup[2] = 64000;
|
||||||
|
$AACsampleRateLookup[3] = 48000;
|
||||||
|
$AACsampleRateLookup[4] = 44100;
|
||||||
|
$AACsampleRateLookup[5] = 32000;
|
||||||
|
$AACsampleRateLookup[6] = 24000;
|
||||||
|
$AACsampleRateLookup[7] = 22050;
|
||||||
|
$AACsampleRateLookup[8] = 16000;
|
||||||
|
$AACsampleRateLookup[9] = 12000;
|
||||||
|
$AACsampleRateLookup[10] = 11025;
|
||||||
|
$AACsampleRateLookup[11] = 8000;
|
||||||
|
$AACsampleRateLookup[12] = 0;
|
||||||
|
$AACsampleRateLookup[13] = 0;
|
||||||
|
$AACsampleRateLookup[14] = 0;
|
||||||
|
$AACsampleRateLookup[15] = 0;
|
||||||
|
}
|
||||||
|
return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AACprofileLookup($profileid, $mpegversion) {
|
||||||
|
static $AACprofileLookup = array();
|
||||||
|
if (empty($AACprofileLookup)) {
|
||||||
|
$AACprofileLookup[2][0] = 'Main profile';
|
||||||
|
$AACprofileLookup[2][1] = 'Low Complexity profile (LC)';
|
||||||
|
$AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)';
|
||||||
|
$AACprofileLookup[2][3] = '(reserved)';
|
||||||
|
$AACprofileLookup[4][0] = 'AAC_MAIN';
|
||||||
|
$AACprofileLookup[4][1] = 'AAC_LC';
|
||||||
|
$AACprofileLookup[4][2] = 'AAC_SSR';
|
||||||
|
$AACprofileLookup[4][3] = 'AAC_LTP';
|
||||||
|
}
|
||||||
|
return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AACchannelCountCalculate($program_configs) {
|
||||||
|
$channels = 0;
|
||||||
|
for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
|
||||||
|
$channels++;
|
||||||
|
if ($program_configs['front_element_is_cpe'][$i]) {
|
||||||
|
// each front element is channel pair (CPE = Channel Pair Element)
|
||||||
|
$channels++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
|
||||||
|
$channels++;
|
||||||
|
if ($program_configs['side_element_is_cpe'][$i]) {
|
||||||
|
// each side element is channel pair (CPE = Channel Pair Element)
|
||||||
|
$channels++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
|
||||||
|
$channels++;
|
||||||
|
if ($program_configs['back_element_is_cpe'][$i]) {
|
||||||
|
// each back element is channel pair (CPE = Channel Pair Element)
|
||||||
|
$channels++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
|
||||||
|
$channels++;
|
||||||
|
}
|
||||||
|
return $channels;
|
||||||
|
}
|
||||||
|
|
||||||
public static function AACchannelCountCalculate($program_configs)
|
|
||||||
{
|
|
||||||
$channels = 0;
|
|
||||||
for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
|
|
||||||
$channels++;
|
|
||||||
if ($program_configs['front_element_is_cpe'][$i]) {
|
|
||||||
// each front element is channel pair (CPE = Channel Pair Element)
|
|
||||||
$channels++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
|
|
||||||
$channels++;
|
|
||||||
if ($program_configs['side_element_is_cpe'][$i]) {
|
|
||||||
// each side element is channel pair (CPE = Channel Pair Element)
|
|
||||||
$channels++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
|
|
||||||
$channels++;
|
|
||||||
if ($program_configs['back_element_is_cpe'][$i]) {
|
|
||||||
// each back element is channel pair (CPE = Channel Pair Element)
|
|
||||||
$channels++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
|
|
||||||
$channels++;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,81 +18,79 @@
|
||||||
class getid3_amr extends getid3_handler
|
class getid3_amr extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$AMRheader = $this->fread(6);
|
$AMRheader = $this->fread(6);
|
||||||
|
|
||||||
$magic = '#!AMR'."\x0A";
|
$magic = '#!AMR'."\x0A";
|
||||||
if (substr($AMRheader, 0, 6) != $magic) {
|
if (substr($AMRheader, 0, 6) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AMRheader, 0, 6)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AMRheader, 0, 6)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['amr'] = [];
|
$info['amr'] = array();
|
||||||
$thisfile_amr = &$info['amr'];
|
$thisfile_amr = &$info['amr'];
|
||||||
|
|
||||||
$info['fileformat'] = 'amr';
|
$info['fileformat'] = 'amr';
|
||||||
$info['audio']['dataformat'] = 'amr';
|
$info['audio']['dataformat'] = 'amr';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr'; // within a small predefined range: 4.75kbps to 12.2kbps
|
$info['audio']['bitrate_mode'] = 'vbr'; // within a small predefined range: 4.75kbps to 12.2kbps
|
||||||
$info['audio']['bits_per_sample'] = 13; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
|
$info['audio']['bits_per_sample'] = 13; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
|
||||||
$info['audio']['sample_rate'] = 8000; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
|
$info['audio']['sample_rate'] = 8000; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
|
||||||
$info['audio']['channels'] = 1;
|
$info['audio']['channels'] = 1;
|
||||||
$thisfile_amr['frame_mode_count'] = [0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0];
|
$thisfile_amr['frame_mode_count'] = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0);
|
||||||
|
|
||||||
$buffer = '';
|
$buffer = '';
|
||||||
do {
|
do {
|
||||||
if ((strlen($buffer) < $this->getid3->fread_buffer_size()) && !feof($this->getid3->fp)) {
|
if ((strlen($buffer) < $this->getid3->fread_buffer_size()) && !feof($this->getid3->fp)) {
|
||||||
$buffer .= $this->fread($this->getid3->fread_buffer_size() * 2);
|
$buffer .= $this->fread($this->getid3->fread_buffer_size() * 2);
|
||||||
}
|
}
|
||||||
$AMR_frame_header = ord(substr($buffer, 0, 1));
|
$AMR_frame_header = ord(substr($buffer, 0, 1));
|
||||||
$codec_mode_request = ($AMR_frame_header & 0x78) >> 3; // The 2nd bit through 5th bit (counting the most significant bit as the first bit) comprise the CMR (Codec Mode Request), values 0-7 being valid for AMR. The top bit of the CMR can actually be ignored, though it is used when AMR forms RTP payloads. The lower 3-bits of the header are reserved and are not used. Viewing the header from most significant bit to least significant bit, the encoding is XCCCCXXX, where Xs are reserved (typically 0) and the Cs are the CMR.
|
$codec_mode_request = ($AMR_frame_header & 0x78) >> 3; // The 2nd bit through 5th bit (counting the most significant bit as the first bit) comprise the CMR (Codec Mode Request), values 0-7 being valid for AMR. The top bit of the CMR can actually be ignored, though it is used when AMR forms RTP payloads. The lower 3-bits of the header are reserved and are not used. Viewing the header from most significant bit to least significant bit, the encoding is XCCCCXXX, where Xs are reserved (typically 0) and the Cs are the CMR.
|
||||||
if ($codec_mode_request > 7) {
|
if ($codec_mode_request > 7) {
|
||||||
$info['error'][] = '';
|
break;
|
||||||
break;
|
}
|
||||||
}
|
$thisfile_amr['frame_mode_count'][$codec_mode_request]++;
|
||||||
$thisfile_amr['frame_mode_count'][$codec_mode_request]++;
|
$buffer = substr($buffer, $this->amr_mode_bytes_per_frame($codec_mode_request));
|
||||||
$buffer = substr($buffer, $this->amr_mode_bytes_per_frame($codec_mode_request));
|
} while (strlen($buffer) > 0);
|
||||||
} while (strlen($buffer) > 0);
|
|
||||||
|
|
||||||
$info['playtime_seconds'] = array_sum($thisfile_amr['frame_mode_count']) * 0.020; // each frame contain 160 samples and is 20 milliseconds long
|
$info['playtime_seconds'] = array_sum($thisfile_amr['frame_mode_count']) * 0.020; // each frame contain 160 samples and is 20 milliseconds long
|
||||||
$info['audio']['bitrate'] = (8 * ($info['avdataend'] - $info['avdataoffset'])) / $info['playtime_seconds']; // bitrate could be calculated from average bitrate by distributation of frame types. That would give effective audio bitrate, this gives overall file bitrate which will be a little bit higher since every frame will waste 8 bits for header, plus a few bits for octet padding
|
$info['audio']['bitrate'] = (8 * ($info['avdataend'] - $info['avdataoffset'])) / $info['playtime_seconds']; // bitrate could be calculated from average bitrate by distributation of frame types. That would give effective audio bitrate, this gives overall file bitrate which will be a little bit higher since every frame will waste 8 bits for header, plus a few bits for octet padding
|
||||||
$info['bitrate'] = $info['audio']['bitrate'];
|
$info['bitrate'] = $info['audio']['bitrate'];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function amr_mode_bitrate($key)
|
public function amr_mode_bitrate($key) {
|
||||||
{
|
static $amr_mode_bitrate = array(
|
||||||
static $amr_mode_bitrate = [
|
0 => 4750,
|
||||||
0 => 4750,
|
1 => 5150,
|
||||||
1 => 5150,
|
2 => 5900,
|
||||||
2 => 5900,
|
3 => 6700,
|
||||||
3 => 6700,
|
4 => 7400,
|
||||||
4 => 7400,
|
5 => 7950,
|
||||||
5 => 7950,
|
6 => 10200,
|
||||||
6 => 10200,
|
7 => 12200,
|
||||||
7 => 12200,
|
);
|
||||||
];
|
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
|
||||||
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
|
}
|
||||||
}
|
|
||||||
|
public function amr_mode_bytes_per_frame($key) {
|
||||||
|
static $amr_mode_bitrate = array(
|
||||||
|
0 => 13, // 1-byte frame header + 95 bits [padded to: 12 bytes] audio data
|
||||||
|
1 => 14, // 1-byte frame header + 103 bits [padded to: 13 bytes] audio data
|
||||||
|
2 => 16, // 1-byte frame header + 118 bits [padded to: 15 bytes] audio data
|
||||||
|
3 => 18, // 1-byte frame header + 134 bits [padded to: 17 bytes] audio data
|
||||||
|
4 => 20, // 1-byte frame header + 148 bits [padded to: 19 bytes] audio data
|
||||||
|
5 => 21, // 1-byte frame header + 159 bits [padded to: 20 bytes] audio data
|
||||||
|
6 => 27, // 1-byte frame header + 204 bits [padded to: 26 bytes] audio data
|
||||||
|
7 => 32, // 1-byte frame header + 244 bits [padded to: 31 bytes] audio data
|
||||||
|
);
|
||||||
|
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function amr_mode_bytes_per_frame($key)
|
|
||||||
{
|
|
||||||
static $amr_mode_bitrate = [
|
|
||||||
0 => 13, // 1-byte frame header + 95 bits [padded to: 12 bytes] audio data
|
|
||||||
1 => 14, // 1-byte frame header + 103 bits [padded to: 13 bytes] audio data
|
|
||||||
2 => 16, // 1-byte frame header + 118 bits [padded to: 15 bytes] audio data
|
|
||||||
3 => 18, // 1-byte frame header + 134 bits [padded to: 17 bytes] audio data
|
|
||||||
4 => 20, // 1-byte frame header + 148 bits [padded to: 19 bytes] audio data
|
|
||||||
5 => 21, // 1-byte frame header + 159 bits [padded to: 20 bytes] audio data
|
|
||||||
6 => 27, // 1-byte frame header + 204 bits [padded to: 26 bytes] audio data
|
|
||||||
7 => 32, // 1-byte frame header + 244 bits [padded to: 31 bytes] audio data
|
|
||||||
];
|
|
||||||
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,149 +18,146 @@
|
||||||
class getid3_au extends getid3_handler
|
class getid3_au extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$AUheader = $this->fread(8);
|
$AUheader = $this->fread(8);
|
||||||
|
|
||||||
$magic = '.snd';
|
$magic = '.snd';
|
||||||
if (substr($AUheader, 0, 4) != $magic) {
|
if (substr($AUheader, 0, 4) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" (".snd") at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AUheader, 0, 4)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" (".snd") at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AUheader, 0, 4)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['au'] = [];
|
$info['au'] = array();
|
||||||
$thisfile_au = &$info['au'];
|
$thisfile_au = &$info['au'];
|
||||||
|
|
||||||
$info['fileformat'] = 'au';
|
$info['fileformat'] = 'au';
|
||||||
$info['audio']['dataformat'] = 'au';
|
$info['audio']['dataformat'] = 'au';
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
$thisfile_au['encoding'] = 'ISO-8859-1';
|
$thisfile_au['encoding'] = 'ISO-8859-1';
|
||||||
|
|
||||||
$thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
|
$thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
|
||||||
$AUheader .= $this->fread($thisfile_au['header_length'] - 8);
|
$AUheader .= $this->fread($thisfile_au['header_length'] - 8);
|
||||||
$info['avdataoffset'] += $thisfile_au['header_length'];
|
$info['avdataoffset'] += $thisfile_au['header_length'];
|
||||||
|
|
||||||
$thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
|
$thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
|
||||||
$thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
|
$thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
|
||||||
$thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
|
$thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
|
||||||
$thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
|
$thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
|
||||||
$thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
|
$thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
|
||||||
|
|
||||||
$thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
|
$thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
|
||||||
$thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
|
$thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
|
||||||
if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
|
if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
|
||||||
$info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
|
||||||
} else {
|
} else {
|
||||||
unset($thisfile_au['bits_per_sample']);
|
unset($thisfile_au['bits_per_sample']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
|
$info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
|
||||||
$info['audio']['channels'] = $thisfile_au['channels'];
|
$info['audio']['channels'] = $thisfile_au['channels'];
|
||||||
|
|
||||||
if (($info['avdataoffset'] + $thisfile_au['data_size']) > $info['avdataend']) {
|
if (($info['avdataoffset'] + $thisfile_au['data_size']) > $info['avdataend']) {
|
||||||
$info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"';
|
$this->warning('Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"');
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
|
$info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
|
||||||
$info['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $info['playtime_seconds'];
|
$info['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AUdataFormatNameLookup($id)
|
public function AUdataFormatNameLookup($id) {
|
||||||
{
|
static $AUdataFormatNameLookup = array(
|
||||||
static $AUdataFormatNameLookup = [
|
0 => 'unspecified format',
|
||||||
0 => 'unspecified format',
|
1 => '8-bit mu-law',
|
||||||
1 => '8-bit mu-law',
|
2 => '8-bit linear',
|
||||||
2 => '8-bit linear',
|
3 => '16-bit linear',
|
||||||
3 => '16-bit linear',
|
4 => '24-bit linear',
|
||||||
4 => '24-bit linear',
|
5 => '32-bit linear',
|
||||||
5 => '32-bit linear',
|
6 => 'floating-point',
|
||||||
6 => 'floating-point',
|
7 => 'double-precision float',
|
||||||
7 => 'double-precision float',
|
8 => 'fragmented sampled data',
|
||||||
8 => 'fragmented sampled data',
|
9 => 'SUN_FORMAT_NESTED',
|
||||||
9 => 'SUN_FORMAT_NESTED',
|
10 => 'DSP program',
|
||||||
10 => 'DSP program',
|
11 => '8-bit fixed-point',
|
||||||
11 => '8-bit fixed-point',
|
12 => '16-bit fixed-point',
|
||||||
12 => '16-bit fixed-point',
|
13 => '24-bit fixed-point',
|
||||||
13 => '24-bit fixed-point',
|
14 => '32-bit fixed-point',
|
||||||
14 => '32-bit fixed-point',
|
|
||||||
|
|
||||||
16 => 'non-audio display data',
|
16 => 'non-audio display data',
|
||||||
17 => 'SND_FORMAT_MULAW_SQUELCH',
|
17 => 'SND_FORMAT_MULAW_SQUELCH',
|
||||||
18 => '16-bit linear with emphasis',
|
18 => '16-bit linear with emphasis',
|
||||||
19 => '16-bit linear with compression',
|
19 => '16-bit linear with compression',
|
||||||
20 => '16-bit linear with emphasis + compression',
|
20 => '16-bit linear with emphasis + compression',
|
||||||
21 => 'Music Kit DSP commands',
|
21 => 'Music Kit DSP commands',
|
||||||
22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
|
22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
|
||||||
23 => 'CCITT g.721 4-bit ADPCM',
|
23 => 'CCITT g.721 4-bit ADPCM',
|
||||||
24 => 'CCITT g.722 ADPCM',
|
24 => 'CCITT g.722 ADPCM',
|
||||||
25 => 'CCITT g.723 3-bit ADPCM',
|
25 => 'CCITT g.723 3-bit ADPCM',
|
||||||
26 => 'CCITT g.723 5-bit ADPCM',
|
26 => 'CCITT g.723 5-bit ADPCM',
|
||||||
27 => 'A-Law 8-bit'
|
27 => 'A-Law 8-bit'
|
||||||
];
|
);
|
||||||
return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
|
return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AUdataFormatBitsPerSampleLookup($id)
|
public function AUdataFormatBitsPerSampleLookup($id) {
|
||||||
{
|
static $AUdataFormatBitsPerSampleLookup = array(
|
||||||
static $AUdataFormatBitsPerSampleLookup = [
|
1 => 8,
|
||||||
1 => 8,
|
2 => 8,
|
||||||
2 => 8,
|
3 => 16,
|
||||||
3 => 16,
|
4 => 24,
|
||||||
4 => 24,
|
5 => 32,
|
||||||
5 => 32,
|
6 => 32,
|
||||||
6 => 32,
|
7 => 64,
|
||||||
7 => 64,
|
|
||||||
|
|
||||||
11 => 8,
|
11 => 8,
|
||||||
12 => 16,
|
12 => 16,
|
||||||
13 => 24,
|
13 => 24,
|
||||||
14 => 32,
|
14 => 32,
|
||||||
|
|
||||||
18 => 16,
|
18 => 16,
|
||||||
19 => 16,
|
19 => 16,
|
||||||
20 => 16,
|
20 => 16,
|
||||||
|
|
||||||
23 => 16,
|
23 => 16,
|
||||||
|
|
||||||
25 => 16,
|
25 => 16,
|
||||||
26 => 16,
|
26 => 16,
|
||||||
27 => 8
|
27 => 8
|
||||||
];
|
);
|
||||||
return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
|
return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AUdataFormatUsedBitsPerSampleLookup($id)
|
public function AUdataFormatUsedBitsPerSampleLookup($id) {
|
||||||
{
|
static $AUdataFormatUsedBitsPerSampleLookup = array(
|
||||||
static $AUdataFormatUsedBitsPerSampleLookup = [
|
1 => 8,
|
||||||
1 => 8,
|
2 => 8,
|
||||||
2 => 8,
|
3 => 16,
|
||||||
3 => 16,
|
4 => 24,
|
||||||
4 => 24,
|
5 => 32,
|
||||||
5 => 32,
|
6 => 32,
|
||||||
6 => 32,
|
7 => 64,
|
||||||
7 => 64,
|
|
||||||
|
|
||||||
11 => 8,
|
11 => 8,
|
||||||
12 => 16,
|
12 => 16,
|
||||||
13 => 24,
|
13 => 24,
|
||||||
14 => 32,
|
14 => 32,
|
||||||
|
|
||||||
18 => 16,
|
18 => 16,
|
||||||
19 => 16,
|
19 => 16,
|
||||||
20 => 16,
|
20 => 16,
|
||||||
|
|
||||||
23 => 4,
|
23 => 4,
|
||||||
|
|
||||||
|
25 => 3,
|
||||||
|
26 => 5,
|
||||||
|
27 => 8,
|
||||||
|
);
|
||||||
|
return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
|
||||||
|
}
|
||||||
|
|
||||||
25 => 3,
|
|
||||||
26 => 5,
|
|
||||||
27 => 8,
|
|
||||||
];
|
|
||||||
return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,108 +18,108 @@
|
||||||
class getid3_avr extends getid3_handler
|
class getid3_avr extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
|
// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
|
||||||
// http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
|
// http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
|
||||||
// offset type length name comments
|
// offset type length name comments
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// 0 char 4 ID format ID == "2BIT"
|
// 0 char 4 ID format ID == "2BIT"
|
||||||
// 4 char 8 name sample name (unused space filled with 0)
|
// 4 char 8 name sample name (unused space filled with 0)
|
||||||
// 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo
|
// 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo
|
||||||
// With stereo, samples are alternated,
|
// With stereo, samples are alternated,
|
||||||
// the first voice is the left :
|
// the first voice is the left :
|
||||||
// (LRLRLRLRLRLRLRLRLR...)
|
// (LRLRLRLRLRLRLRLRLR...)
|
||||||
// 14 short 1 resolution 8, 12 or 16 (bits)
|
// 14 short 1 resolution 8, 12 or 16 (bits)
|
||||||
// 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed
|
// 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed
|
||||||
// 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on
|
// 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on
|
||||||
// 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127
|
// 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127
|
||||||
// 0xFFFF means "no MIDI note defined"
|
// 0xFFFF means "no MIDI note defined"
|
||||||
// 22 byte 1 Replay speed Frequence in the Replay software
|
// 22 byte 1 Replay speed Frequence in the Replay software
|
||||||
// 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
|
// 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
|
||||||
// 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
|
// 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
|
||||||
// 6=43.885 Khz, 7=47.261 Khz
|
// 6=43.885 Khz, 7=47.261 Khz
|
||||||
// -1 (0xFF)=no defined Frequence
|
// -1 (0xFF)=no defined Frequence
|
||||||
// 23 byte 3 sample rate in Hertz
|
// 23 byte 3 sample rate in Hertz
|
||||||
// 26 long 1 size in bytes (2 * bytes in stereo)
|
// 26 long 1 size in bytes (2 * bytes in stereo)
|
||||||
// 30 long 1 loop begin 0 for no loop
|
// 30 long 1 loop begin 0 for no loop
|
||||||
// 34 long 1 loop size equal to 'size' for no loop
|
// 34 long 1 loop size equal to 'size' for no loop
|
||||||
// 38 short 2 Reserved, MIDI keyboard split */
|
// 38 short 2 Reserved, MIDI keyboard split */
|
||||||
// 40 short 2 Reserved, sample compression */
|
// 40 short 2 Reserved, sample compression */
|
||||||
// 42 short 2 Reserved */
|
// 42 short 2 Reserved */
|
||||||
// 44 char 20; Additional filename space, used if (name[7] != 0)
|
// 44 char 20; Additional filename space, used if (name[7] != 0)
|
||||||
// 64 byte 64 user data
|
// 64 byte 64 user data
|
||||||
// 128 bytes ? sample data (12 bits samples are coded on 16 bits:
|
// 128 bytes ? sample data (12 bits samples are coded on 16 bits:
|
||||||
// 0000 xxxx xxxx xxxx)
|
// 0000 xxxx xxxx xxxx)
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
// Note that all values are in motorola (big-endian) format, and that long is
|
// Note that all values are in motorola (big-endian) format, and that long is
|
||||||
// assumed to be 4 bytes, and short 2 bytes.
|
// assumed to be 4 bytes, and short 2 bytes.
|
||||||
// When reading the samples, you should handle both signed and unsigned data,
|
// 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
|
// and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
|
||||||
// 8-bit data between signed/unsigned just add 127 to the sample values.
|
// 8-bit data between signed/unsigned just add 127 to the sample values.
|
||||||
// Simularly for 16-bit data you should add 32769
|
// Simularly for 16-bit data you should add 32769
|
||||||
|
|
||||||
$info['fileformat'] = 'avr';
|
$info['fileformat'] = 'avr';
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$AVRheader = $this->fread(128);
|
$AVRheader = $this->fread(128);
|
||||||
|
|
||||||
$info['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
|
$info['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
|
||||||
$magic = '2BIT';
|
$magic = '2BIT';
|
||||||
if ($info['avr']['raw']['magic'] != $magic) {
|
if ($info['avr']['raw']['magic'] != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['avr']['raw']['magic']).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['avr']['raw']['magic']).'"');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['avr']);
|
unset($info['avr']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['avdataoffset'] += 128;
|
$info['avdataoffset'] += 128;
|
||||||
|
|
||||||
$info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
|
$info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
|
||||||
$info['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
|
$info['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
|
||||||
$info['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
|
$info['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
|
||||||
$info['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
|
$info['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
|
||||||
$info['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
|
$info['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
|
||||||
$info['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
|
$info['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
|
||||||
$info['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
|
$info['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
|
||||||
$info['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
|
$info['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
|
||||||
$info['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
|
$info['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
|
||||||
$info['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
|
$info['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
|
||||||
$info['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
|
$info['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
|
||||||
$info['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
|
$info['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
|
||||||
$info['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
|
$info['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
|
||||||
$info['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
|
$info['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
|
||||||
$info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
|
$info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
|
||||||
$info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
|
$info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
|
||||||
|
|
||||||
$info['avr']['flags']['stereo'] = (($info['avr']['raw']['mono'] == 0) ? false : true);
|
$info['avr']['flags']['stereo'] = (($info['avr']['raw']['mono'] == 0) ? false : true);
|
||||||
$info['avr']['flags']['signed'] = (($info['avr']['raw']['signed'] == 0) ? false : true);
|
$info['avr']['flags']['signed'] = (($info['avr']['raw']['signed'] == 0) ? false : true);
|
||||||
$info['avr']['flags']['loop'] = (($info['avr']['raw']['loop'] == 0) ? false : true);
|
$info['avr']['flags']['loop'] = (($info['avr']['raw']['loop'] == 0) ? false : true);
|
||||||
|
|
||||||
$info['avr']['midi_notes'] = [];
|
$info['avr']['midi_notes'] = array();
|
||||||
if (($info['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
|
if (($info['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
|
||||||
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xFF00) >> 8;
|
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xFF00) >> 8;
|
||||||
}
|
}
|
||||||
if (($info['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
|
if (($info['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
|
||||||
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0x00FF);
|
$info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0x00FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($info['avdataend'] - $info['avdataoffset']) != ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
|
if (($info['avdataend'] - $info['avdataoffset']) != ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
|
||||||
$info['warning'][] = 'Probable truncated file: expecting '.($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']);
|
$this->warning('Probable truncated file: expecting '.($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['audio']['dataformat'] = 'avr';
|
$info['audio']['dataformat'] = 'avr';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
$info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample'];
|
||||||
$info['audio']['sample_rate'] = $info['avr']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['avr']['sample_rate'];
|
||||||
$info['audio']['channels'] = ($info['avr']['flags']['stereo'] ? 2 : 1);
|
$info['audio']['channels'] = ($info['avr']['flags']['stereo'] ? 2 : 1);
|
||||||
$info['playtime_seconds'] = ($info['avr']['sample_length'] / $info['audio']['channels']) / $info['avr']['sample_rate'];
|
$info['playtime_seconds'] = ($info['avr']['sample_length'] / $info['audio']['channels']) / $info['avr']['sample_rate'];
|
||||||
$info['audio']['bitrate'] = ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $info['playtime_seconds'];
|
$info['audio']['bitrate'] = ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $info['playtime_seconds'];
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,207 +17,212 @@
|
||||||
|
|
||||||
class getid3_bonk extends getid3_handler
|
class getid3_bonk extends getid3_handler
|
||||||
{
|
{
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['bonk'] = [];
|
$info['bonk'] = array();
|
||||||
$thisfile_bonk = &$info['bonk'];
|
$thisfile_bonk = &$info['bonk'];
|
||||||
|
|
||||||
$thisfile_bonk['dataoffset'] = $info['avdataoffset'];
|
$thisfile_bonk['dataoffset'] = $info['avdataoffset'];
|
||||||
$thisfile_bonk['dataend'] = $info['avdataend'];
|
$thisfile_bonk['dataend'] = $info['avdataend'];
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($thisfile_bonk['dataend'])) {
|
if (!getid3_lib::intValueSupported($thisfile_bonk['dataend'])) {
|
||||||
$info['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to '.round(PHP_INT_MAX / 1073741824).'GB';
|
|
||||||
} else {
|
|
||||||
// scan-from-end method, for v0.6 and higher
|
|
||||||
$this->fseek($thisfile_bonk['dataend'] - 8);
|
|
||||||
$PossibleBonkTag = $this->fread(8);
|
|
||||||
while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
|
|
||||||
$BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
|
|
||||||
$this->fseek(0 - $BonkTagSize, SEEK_CUR);
|
|
||||||
$BonkTagOffset = $this->ftell();
|
|
||||||
$TagHeaderTest = $this->fread(5);
|
|
||||||
if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
|
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
|
||||||
|
|
||||||
$thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
|
$this->warning('Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to '.round(PHP_INT_MAX / 1073741824).'GB');
|
||||||
$thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
|
|
||||||
$this->HandleBonkTags($BonkTagName);
|
|
||||||
$NextTagEndOffset = $BonkTagOffset - 8;
|
|
||||||
if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
|
|
||||||
if (empty($info['audio']['encoder'])) {
|
|
||||||
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$this->fseek($NextTagEndOffset);
|
|
||||||
$PossibleBonkTag = $this->fread(8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// seek-from-beginning method for v0.4 and v0.5
|
} else {
|
||||||
if (empty($thisfile_bonk['BONK'])) {
|
|
||||||
$this->fseek($thisfile_bonk['dataoffset']);
|
|
||||||
do {
|
|
||||||
$TagHeaderTest = $this->fread(5);
|
|
||||||
switch ($TagHeaderTest) {
|
|
||||||
case "\x00".'BONK':
|
|
||||||
if (empty($info['audio']['encoder'])) {
|
|
||||||
$info['audio']['encoder'] = 'BONK v0.4';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "\x00".'INFO':
|
// scan-from-end method, for v0.6 and higher
|
||||||
$info['audio']['encoder'] = 'Extended BONK v0.5';
|
$this->fseek($thisfile_bonk['dataend'] - 8);
|
||||||
break;
|
$PossibleBonkTag = $this->fread(8);
|
||||||
|
while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
|
||||||
|
$BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
|
||||||
|
$this->fseek(0 - $BonkTagSize, SEEK_CUR);
|
||||||
|
$BonkTagOffset = $this->ftell();
|
||||||
|
$TagHeaderTest = $this->fread(5);
|
||||||
|
if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
|
||||||
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||||
|
|
||||||
default:
|
$thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
|
||||||
break 2;
|
$thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
|
||||||
}
|
$this->HandleBonkTags($BonkTagName);
|
||||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
$NextTagEndOffset = $BonkTagOffset - 8;
|
||||||
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
|
||||||
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
if (empty($info['audio']['encoder'])) {
|
||||||
$this->HandleBonkTags($BonkTagName);
|
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||||
} while (true);
|
}
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
$this->fseek($NextTagEndOffset);
|
||||||
|
$PossibleBonkTag = $this->fread(8);
|
||||||
|
}
|
||||||
|
|
||||||
// parse META block for v0.6 - v0.8
|
}
|
||||||
if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
|
|
||||||
$this->fseek($thisfile_bonk['META']['tags']['info']);
|
|
||||||
$TagHeaderTest = $this->fread(5);
|
|
||||||
if ($TagHeaderTest == "\x00".'INFO') {
|
|
||||||
$info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
|
|
||||||
|
|
||||||
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
// seek-from-beginning method for v0.4 and v0.5
|
||||||
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
if (empty($thisfile_bonk['BONK'])) {
|
||||||
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
$this->fseek($thisfile_bonk['dataoffset']);
|
||||||
$this->HandleBonkTags($BonkTagName);
|
do {
|
||||||
}
|
$TagHeaderTest = $this->fread(5);
|
||||||
}
|
switch ($TagHeaderTest) {
|
||||||
|
case "\x00".'BONK':
|
||||||
|
if (empty($info['audio']['encoder'])) {
|
||||||
|
$info['audio']['encoder'] = 'BONK v0.4';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (empty($info['audio']['encoder'])) {
|
case "\x00".'INFO':
|
||||||
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
$info['audio']['encoder'] = 'Extended BONK v0.5';
|
||||||
}
|
break;
|
||||||
if (empty($thisfile_bonk['BONK'])) {
|
|
||||||
unset($info['bonk']);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function HandleBonkTags($BonkTagName)
|
default:
|
||||||
{
|
break 2;
|
||||||
$info = &$this->getid3->info;
|
}
|
||||||
switch ($BonkTagName) {
|
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||||
case 'BONK':
|
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
||||||
// shortcut
|
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
||||||
$thisfile_bonk_BONK = &$info['bonk']['BONK'];
|
$this->HandleBonkTags($BonkTagName);
|
||||||
|
|
||||||
$BonkData = "\x00".'BONK'.$this->fread(17);
|
} while (true);
|
||||||
$thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
}
|
||||||
$thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4));
|
|
||||||
$thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4));
|
|
||||||
|
|
||||||
$thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1));
|
// parse META block for v0.6 - v0.8
|
||||||
$thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1));
|
if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
|
||||||
$thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1));
|
$this->fseek($thisfile_bonk['META']['tags']['info']);
|
||||||
$thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2));
|
$TagHeaderTest = $this->fread(5);
|
||||||
$thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1));
|
if ($TagHeaderTest == "\x00".'INFO') {
|
||||||
$thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2));
|
$info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
|
||||||
|
|
||||||
$info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
|
$BonkTagName = substr($TagHeaderTest, 1, 4);
|
||||||
$info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
|
$thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
|
||||||
|
$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
|
||||||
|
$this->HandleBonkTags($BonkTagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$info['fileformat'] = 'bonk';
|
if (empty($info['audio']['encoder'])) {
|
||||||
$info['audio']['dataformat'] = 'bonk';
|
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr'; // assumed
|
}
|
||||||
$info['audio']['channels'] = $thisfile_bonk_BONK['channels'];
|
if (empty($thisfile_bonk['BONK'])) {
|
||||||
$info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
|
unset($info['bonk']);
|
||||||
$info['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
|
}
|
||||||
$info['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
|
return true;
|
||||||
$info['audio']['codec'] = 'bonk';
|
|
||||||
|
|
||||||
$info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
|
}
|
||||||
if ($info['playtime_seconds'] > 0) {
|
|
||||||
$info['audio']['bitrate'] = (($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8) / $info['playtime_seconds'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'INFO':
|
public function HandleBonkTags($BonkTagName) {
|
||||||
// shortcut
|
$info = &$this->getid3->info;
|
||||||
$thisfile_bonk_INFO = &$info['bonk']['INFO'];
|
switch ($BonkTagName) {
|
||||||
|
case 'BONK':
|
||||||
|
// shortcut
|
||||||
|
$thisfile_bonk_BONK = &$info['bonk']['BONK'];
|
||||||
|
|
||||||
$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int($this->fread(1));
|
$BonkData = "\x00".'BONK'.$this->fread(17);
|
||||||
$thisfile_bonk_INFO['entries_count'] = 0;
|
$thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
||||||
$NextInfoDataPair = $this->fread(5);
|
$thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4));
|
||||||
if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
$thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4));
|
||||||
while (!feof($this->getid3->fp)) {
|
|
||||||
//$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
|
|
||||||
//$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
|
|
||||||
//$thisfile_bonk_INFO[] = $CurrentSeekInfo;
|
|
||||||
|
|
||||||
$NextInfoDataPair = $this->fread(5);
|
$thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1));
|
||||||
if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
$thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1));
|
||||||
$this->fseek(-5, SEEK_CUR);
|
$thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1));
|
||||||
break;
|
$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_INFO['entries_count']++;
|
$thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2));
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'META':
|
$info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
|
||||||
$BonkData = "\x00".'META'.$this->fread($info['bonk']['META']['size'] - 5);
|
$info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
|
||||||
$info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
|
||||||
|
|
||||||
$MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
|
$info['fileformat'] = 'bonk';
|
||||||
$offset = 6;
|
$info['audio']['dataformat'] = 'bonk';
|
||||||
for ($i = 0; $i < $MetaTagEntries; $i++) {
|
$info['audio']['bitrate_mode'] = 'vbr'; // assumed
|
||||||
$MetaEntryTagName = substr($BonkData, $offset, 4);
|
$info['audio']['channels'] = $thisfile_bonk_BONK['channels'];
|
||||||
$offset += 4;
|
$info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
|
||||||
$MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
|
$info['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
|
||||||
$offset += 4;
|
$info['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
|
||||||
$info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
|
$info['audio']['codec'] = 'bonk';
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ' ID3':
|
$info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
|
||||||
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
if ($info['playtime_seconds'] > 0) {
|
||||||
|
$info['audio']['bitrate'] = (($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// ID3v2 checking is optional
|
case 'INFO':
|
||||||
if (class_exists('getid3_id3v2')) {
|
// shortcut
|
||||||
$getid3_temp = new getID3();
|
$thisfile_bonk_INFO = &$info['bonk']['INFO'];
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
|
||||||
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
|
|
||||||
$getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2;
|
|
||||||
$info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze();
|
|
||||||
if ($info['bonk'][' ID3']['valid']) {
|
|
||||||
$info['id3v2'] = $getid3_temp->info['id3v2'];
|
|
||||||
}
|
|
||||||
unset($getid3_temp, $getid3_id3v2);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int($this->fread(1));
|
||||||
$info['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$info['bonk'][$BonkTagName]['offset'];
|
$thisfile_bonk_INFO['entries_count'] = 0;
|
||||||
break;
|
$NextInfoDataPair = $this->fread(5);
|
||||||
}
|
if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
||||||
}
|
while (!feof($this->getid3->fp)) {
|
||||||
|
//$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
|
||||||
|
//$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
|
||||||
|
//$thisfile_bonk_INFO[] = $CurrentSeekInfo;
|
||||||
|
|
||||||
|
$NextInfoDataPair = $this->fread(5);
|
||||||
|
if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
|
||||||
|
$this->fseek(-5, SEEK_CUR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$thisfile_bonk_INFO['entries_count']++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'META':
|
||||||
|
$BonkData = "\x00".'META'.$this->fread($info['bonk']['META']['size'] - 5);
|
||||||
|
$info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
|
||||||
|
|
||||||
|
$MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
|
||||||
|
$offset = 6;
|
||||||
|
for ($i = 0; $i < $MetaTagEntries; $i++) {
|
||||||
|
$MetaEntryTagName = substr($BonkData, $offset, 4);
|
||||||
|
$offset += 4;
|
||||||
|
$MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
|
||||||
|
$offset += 4;
|
||||||
|
$info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ' ID3':
|
||||||
|
$info['audio']['encoder'] = 'Extended BONK v0.9+';
|
||||||
|
|
||||||
|
// ID3v2 checking is optional
|
||||||
|
if (class_exists('getid3_id3v2')) {
|
||||||
|
$getid3_temp = new getID3();
|
||||||
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
|
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
|
||||||
|
$getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2;
|
||||||
|
$info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze();
|
||||||
|
if ($info['bonk'][' ID3']['valid']) {
|
||||||
|
$info['id3v2'] = $getid3_temp->info['id3v2'];
|
||||||
|
}
|
||||||
|
unset($getid3_temp, $getid3_id3v2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->warning('Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$info['bonk'][$BonkTagName]['offset']);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
|
||||||
|
static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
|
||||||
|
foreach ($BonkIsValidTagName as $validtagname) {
|
||||||
|
if ($validtagname == $PossibleBonkTag) {
|
||||||
|
return true;
|
||||||
|
} elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase = false)
|
|
||||||
{
|
|
||||||
static $BonkIsValidTagName = ['BONK', 'INFO', ' ID3', 'META'];
|
|
||||||
foreach ($BonkIsValidTagName as $validtagname) {
|
|
||||||
if ($validtagname == $PossibleBonkTag) {
|
|
||||||
return true;
|
|
||||||
} elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
133
app/Library/getid3/getid3/module.audio.dsf.php
Normal file
133
app/Library/getid3/getid3/module.audio.dsf.php
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
<?php
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
/// getID3() by James Heinrich <info@getid3.org> //
|
||||||
|
// available at http://getid3.sourceforge.net //
|
||||||
|
// or http://www.getid3.org //
|
||||||
|
// also https://github.com/JamesHeinrich/getID3 //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// See readme.txt for more details //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// module.audio.dsf.php //
|
||||||
|
// module for analyzing dsf/DSF Audio files //
|
||||||
|
// dependencies: module.tag.id3v2.php //
|
||||||
|
// ///
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
|
||||||
|
|
||||||
|
class getid3_dsf extends getid3_handler
|
||||||
|
{
|
||||||
|
|
||||||
|
public function Analyze() {
|
||||||
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
|
$info['fileformat'] = 'dsf';
|
||||||
|
$info['audio']['dataformat'] = 'dsf';
|
||||||
|
$info['audio']['lossless'] = true;
|
||||||
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
|
|
||||||
|
$this->fseek($info['avdataoffset']);
|
||||||
|
$dsfheader = $this->fread(28 + 12);
|
||||||
|
|
||||||
|
$headeroffset = 0;
|
||||||
|
$info['dsf']['dsd']['magic'] = substr($dsfheader, $headeroffset, 4);
|
||||||
|
$headeroffset += 4;
|
||||||
|
$magic = 'DSD ';
|
||||||
|
if ($info['dsf']['dsd']['magic'] != $magic) {
|
||||||
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dsf']['dsd']['magic']).'"');
|
||||||
|
unset($info['fileformat']);
|
||||||
|
unset($info['audio']);
|
||||||
|
unset($info['dsf']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info['dsf']['dsd']['dsd_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // should be 28
|
||||||
|
$headeroffset += 8;
|
||||||
|
$info['dsf']['dsd']['dsf_file_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
|
||||||
|
$headeroffset += 8;
|
||||||
|
$info['dsf']['dsd']['meta_chunk_offset'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
|
||||||
|
$headeroffset += 8;
|
||||||
|
|
||||||
|
|
||||||
|
$info['dsf']['fmt']['magic'] = substr($dsfheader, $headeroffset, 4);
|
||||||
|
$headeroffset += 4;
|
||||||
|
$magic = 'fmt ';
|
||||||
|
if ($info['dsf']['fmt']['magic'] != $magic) {
|
||||||
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['fmt']['magic']).'"');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info['dsf']['fmt']['fmt_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // usually 52 bytes
|
||||||
|
$headeroffset += 8;
|
||||||
|
$dsfheader .= $this->fread($info['dsf']['fmt']['fmt_chunk_size'] - 12 + 12); // we have already read the entire DSD chunk, plus 12 bytes of FMT. We now want to read the size of FMT, plus 12 bytes into the next chunk to get magic and size.
|
||||||
|
if (strlen($dsfheader) != ($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size'] + 12)) {
|
||||||
|
$this->error('Expecting '.($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size']).' bytes header, found '.strlen($dsfheader).' bytes');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info['dsf']['fmt']['format_version'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "1"
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['format_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "0" = "DSD Raw"
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['channel_type_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['channels'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['sample_count'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
|
||||||
|
$headeroffset += 8;
|
||||||
|
$info['dsf']['fmt']['channel_block_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4));
|
||||||
|
$headeroffset += 4;
|
||||||
|
$info['dsf']['fmt']['reserved'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // zero-filled
|
||||||
|
$headeroffset += 4;
|
||||||
|
|
||||||
|
|
||||||
|
$info['dsf']['data']['magic'] = substr($dsfheader, $headeroffset, 4);
|
||||||
|
$headeroffset += 4;
|
||||||
|
$magic = 'data';
|
||||||
|
if ($info['dsf']['data']['magic'] != $magic) {
|
||||||
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['data']['magic']).'"');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info['dsf']['data']['data_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8));
|
||||||
|
$headeroffset += 8;
|
||||||
|
$info['avdataoffset'] = $headeroffset;
|
||||||
|
$info['avdataend'] = $info['avdataoffset'] + $info['dsf']['data']['data_chunk_size'];
|
||||||
|
|
||||||
|
|
||||||
|
if ($info['dsf']['dsd']['meta_chunk_offset'] > 0) {
|
||||||
|
$getid3_id3v2 = new getid3_id3v2($this->getid3);
|
||||||
|
$getid3_id3v2->StartingOffset = $info['dsf']['dsd']['meta_chunk_offset'];
|
||||||
|
$getid3_id3v2->Analyze();
|
||||||
|
unset($getid3_id3v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$info['dsf']['fmt']['channel_type'] = $this->DSFchannelTypeLookup($info['dsf']['fmt']['channel_type_id']);
|
||||||
|
$info['audio']['channelmode'] = $info['dsf']['fmt']['channel_type'];
|
||||||
|
$info['audio']['bits_per_sample'] = $info['dsf']['fmt']['bits_per_sample'];
|
||||||
|
$info['audio']['sample_rate'] = $info['dsf']['fmt']['sample_rate'];
|
||||||
|
$info['audio']['channels'] = $info['dsf']['fmt']['channels'];
|
||||||
|
$info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels'];
|
||||||
|
$info['playtime_seconds'] = ($info['dsf']['data']['data_chunk_size'] * 8) / $info['audio']['bitrate'];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function DSFchannelTypeLookup($channel_type_id) {
|
||||||
|
static $DSFchannelTypeLookup = array(
|
||||||
|
// interleaving order:
|
||||||
|
1 => 'mono', // 1: Mono
|
||||||
|
2 => 'stereo', // 1: Front-Left; 2: Front-Right
|
||||||
|
3 => '3-channel', // 1: Front-Left; 2: Front-Right; 3: Center
|
||||||
|
4 => 'quad', // 1: Front-Left; 2: Front-Right; 3: Back-Left; 4: Back-Right
|
||||||
|
5 => '4-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency
|
||||||
|
6 => '5-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Back-Left 5: Back-Right
|
||||||
|
7 => '5.1', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency; 5: Back-Left; 6: Back-Right
|
||||||
|
);
|
||||||
|
return (isset($DSFchannelTypeLookup[$channel_type_id]) ? $DSFchannelTypeLookup[$channel_type_id] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,78 +18,82 @@
|
||||||
class getid3_dss extends getid3_handler
|
class getid3_dss extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$DSSheader = $this->fread(1540);
|
$DSSheader = $this->fread(1540);
|
||||||
|
|
||||||
if (!preg_match('#^(\x02|\x03)ds[s2]#', $DSSheader)) {
|
if (!preg_match('#^[\\x02-\\x06]ds[s2]#', $DSSheader)) {
|
||||||
$info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
|
$this->error('Expecting "[02-06] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
|
// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
|
||||||
$info['encoding'] = 'ISO-8859-1'; // not certain, but assumed
|
$info['encoding'] = 'ISO-8859-1'; // not certain, but assumed
|
||||||
$info['dss'] = [];
|
$info['dss'] = array();
|
||||||
|
|
||||||
$info['fileformat'] = 'dss';
|
$info['fileformat'] = 'dss';
|
||||||
$info['mime_type'] = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2"
|
$info['mime_type'] = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2"
|
||||||
$info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2"
|
$info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2"
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
|
|
||||||
$info['dss']['version'] = ord(substr($DSSheader, 0, 1));
|
$info['dss']['version'] = ord(substr($DSSheader, 0, 1));
|
||||||
$info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
|
$info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
|
||||||
$info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
|
$info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
|
||||||
// 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
|
// 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
|
||||||
$info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
|
$info['dss']['date_create_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
|
||||||
$info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
|
$info['dss']['date_complete_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
|
||||||
$info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
|
$info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
|
||||||
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
|
if ($info['dss']['version'] <= 3) {
|
||||||
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
|
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
|
||||||
$info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
|
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
|
||||||
$info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
|
$info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
|
||||||
|
$info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
|
||||||
|
$info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
|
||||||
|
} else {
|
||||||
|
$this->getid3->warning('DSS above version 3 not fully supported in this version of getID3. Any additional documentation or format specifications would be welcome. This file is version '.$info['dss']['version']);
|
||||||
|
}
|
||||||
|
|
||||||
$info['audio']['bits_per_sample'] = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
|
$info['audio']['bits_per_sample'] = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
|
||||||
$info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
|
$info['audio']['channels'] = 1;
|
||||||
$info['audio']['channels'] = 1;
|
|
||||||
|
|
||||||
$info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
|
if (!empty($info['dss']['playtime_ms']) && (floor($info['dss']['playtime_ms'] / 1000) == $info['dss']['playtime_sec'])) { // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
|
||||||
if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
|
$info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
|
||||||
// *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
|
} else {
|
||||||
$info['playtime_seconds'] = $info['dss']['playtime_sec'];
|
$info['playtime_seconds'] = $info['dss']['playtime_sec'];
|
||||||
$this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
|
if (!empty($info['dss']['playtime_ms'])) {
|
||||||
}
|
$this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
|
||||||
$info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
|
}
|
||||||
|
}
|
||||||
|
$info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DSSdateStringToUnixDate($datestring)
|
public function DSSdateStringToUnixDate($datestring) {
|
||||||
{
|
$y = substr($datestring, 0, 2);
|
||||||
$y = substr($datestring, 0, 2);
|
$m = substr($datestring, 2, 2);
|
||||||
$m = substr($datestring, 2, 2);
|
$d = substr($datestring, 4, 2);
|
||||||
$d = substr($datestring, 4, 2);
|
$h = substr($datestring, 6, 2);
|
||||||
$h = substr($datestring, 6, 2);
|
$i = substr($datestring, 8, 2);
|
||||||
$i = substr($datestring, 8, 2);
|
$s = substr($datestring, 10, 2);
|
||||||
$s = substr($datestring, 10, 2);
|
$y += (($y < 95) ? 2000 : 1900);
|
||||||
$y += (($y < 95) ? 2000 : 1900);
|
return mktime($h, $i, $s, $m, $d, $y);
|
||||||
return mktime($h, $i, $s, $m, $d, $y);
|
}
|
||||||
}
|
|
||||||
|
public function DSSsampleRateLookup($sample_rate_index) {
|
||||||
|
static $dssSampleRateLookup = array(
|
||||||
|
0x0A => 16000,
|
||||||
|
0x0C => 11025,
|
||||||
|
0x0D => 12000,
|
||||||
|
0x15 => 8000,
|
||||||
|
);
|
||||||
|
if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) {
|
||||||
|
$this->getid3->warning('unknown sample_rate_index: 0x'.strtoupper(dechex($sample_rate_index)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $dssSampleRateLookup[$sample_rate_index];
|
||||||
|
}
|
||||||
|
|
||||||
public function DSSsampleRateLookup($sample_rate_index)
|
|
||||||
{
|
|
||||||
static $dssSampleRateLookup = [
|
|
||||||
0x0A => 16000,
|
|
||||||
0x0C => 11025,
|
|
||||||
0x0D => 12000,
|
|
||||||
0x15 => 8000,
|
|
||||||
];
|
|
||||||
if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) {
|
|
||||||
$this->getid3->warning('unknown sample_rate_index: '.$sample_rate_index);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $dssSampleRateLookup[$sample_rate_index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,273 +20,272 @@
|
||||||
*/
|
*/
|
||||||
class getid3_dts extends getid3_handler
|
class getid3_dts extends getid3_handler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Default DTS syncword used in native .cpt or .dts formats
|
* Default DTS syncword used in native .cpt or .dts formats
|
||||||
*/
|
*/
|
||||||
const syncword = "\x7F\xFE\x80\x01";
|
const syncword = "\x7F\xFE\x80\x01";
|
||||||
|
|
||||||
private $readBinDataOffset = 0;
|
private $readBinDataOffset = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible syncwords indicating bitstream encoding
|
* Possible syncwords indicating bitstream encoding
|
||||||
*/
|
*/
|
||||||
public static $syncwords = [
|
public static $syncwords = array(
|
||||||
0 => "\x7F\xFE\x80\x01", // raw big-endian
|
0 => "\x7F\xFE\x80\x01", // raw big-endian
|
||||||
1 => "\xFE\x7F\x01\x80", // raw little-endian
|
1 => "\xFE\x7F\x01\x80", // raw little-endian
|
||||||
2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
|
2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
|
||||||
3 => "\xFF\x1F\x00\xE8"]; // 14-bit little-endian
|
3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$info['fileformat'] = 'dts';
|
||||||
$info['fileformat'] = 'dts';
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
|
$DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
|
||||||
|
|
||||||
// check syncword
|
// check syncword
|
||||||
$sync = substr($DTSheader, 0, 4);
|
$sync = substr($DTSheader, 0, 4);
|
||||||
if (($encoding = array_search($sync, self::$syncwords)) !== false) {
|
if (($encoding = array_search($sync, self::$syncwords)) !== false) {
|
||||||
$info['dts']['raw']['magic'] = $sync;
|
|
||||||
$this->readBinDataOffset = 32;
|
$info['dts']['raw']['magic'] = $sync;
|
||||||
|
$this->readBinDataOffset = 32;
|
||||||
|
|
||||||
} elseif ($this->isDependencyFor('matroska')) {
|
} elseif ($this->isDependencyFor('matroska')) {
|
||||||
// Matroska contains DTS without syncword encoded as raw big-endian format
|
|
||||||
$encoding = 0;
|
// Matroska contains DTS without syncword encoded as raw big-endian format
|
||||||
$this->readBinDataOffset = 0;
|
$encoding = 0;
|
||||||
|
$this->readBinDataOffset = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
unset($info['fileformat']);
|
|
||||||
return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode header
|
unset($info['fileformat']);
|
||||||
$fhBS = '';
|
return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
|
||||||
for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
|
|
||||||
switch ($encoding) {
|
|
||||||
case 0: // raw big-endian
|
|
||||||
$fhBS .= getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2));
|
|
||||||
break;
|
|
||||||
case 1: // raw little-endian
|
|
||||||
$fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
|
|
||||||
break;
|
|
||||||
case 2: // 14-bit big-endian
|
|
||||||
$fhBS .= substr(getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2)), 2, 14);
|
|
||||||
break;
|
|
||||||
case 3: // 14-bit little-endian
|
|
||||||
$fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
|
}
|
||||||
$info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
|
|
||||||
$info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
|
// decode header
|
||||||
$info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
|
$fhBS = '';
|
||||||
$info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
|
for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
|
||||||
$info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
|
switch ($encoding) {
|
||||||
$info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
|
case 0: // raw big-endian
|
||||||
$info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
|
$fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) );
|
||||||
$info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
|
break;
|
||||||
$info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
|
case 1: // raw little-endian
|
||||||
$info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
|
$fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
|
||||||
$info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
|
break;
|
||||||
$info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
|
case 2: // 14-bit big-endian
|
||||||
$info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
|
$fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14);
|
||||||
$info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
|
break;
|
||||||
$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
|
case 3: // 14-bit little-endian
|
||||||
$info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
|
$fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
|
||||||
$info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
|
break;
|
||||||
if ($info['dts']['flags']['crc_present']) {
|
}
|
||||||
$info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
|
}
|
||||||
}
|
|
||||||
$info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
|
$info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
|
||||||
$info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
|
$info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
|
||||||
$info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
|
$info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
$info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
|
$info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
|
||||||
$info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
|
$info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
|
||||||
$info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
|
$info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
|
||||||
$info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
|
$info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
|
||||||
$info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
|
$info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
|
||||||
|
$info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
|
||||||
|
$info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
|
||||||
|
$info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
if ($info['dts']['flags']['crc_present']) {
|
||||||
|
$info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
|
||||||
|
}
|
||||||
|
$info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
|
||||||
|
$info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
|
||||||
|
$info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
|
||||||
|
$info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
|
||||||
|
$info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
|
||||||
|
|
||||||
|
|
||||||
$info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
|
$info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
|
||||||
$info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
|
$info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
|
||||||
$info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
|
$info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
|
||||||
$info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
|
$info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
|
||||||
$info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false);
|
$info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false);
|
||||||
$info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
|
$info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
|
||||||
$info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
|
$info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
|
||||||
$info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
|
$info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
|
||||||
|
|
||||||
$info['audio']['dataformat'] = 'dts';
|
$info['audio']['dataformat'] = 'dts';
|
||||||
$info['audio']['lossless'] = $info['dts']['flags']['lossless'];
|
$info['audio']['lossless'] = $info['dts']['flags']['lossless'];
|
||||||
$info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
|
$info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
|
||||||
$info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
|
||||||
$info['audio']['sample_rate'] = $info['dts']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['dts']['sample_rate'];
|
||||||
$info['audio']['channels'] = $info['dts']['channels'];
|
$info['audio']['channels'] = $info['dts']['channels'];
|
||||||
$info['audio']['bitrate'] = $info['dts']['bitrate'];
|
$info['audio']['bitrate'] = $info['dts']['bitrate'];
|
||||||
if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
|
if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
|
||||||
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
|
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
|
||||||
if (($encoding == 2) || ($encoding == 3)) {
|
if (($encoding == 2) || ($encoding == 3)) {
|
||||||
// 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
|
// 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
|
||||||
$info['playtime_seconds'] *= (14 / 16);
|
$info['playtime_seconds'] *= (14 / 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function readBinData($bin, $length)
|
private function readBinData($bin, $length) {
|
||||||
{
|
$data = substr($bin, $this->readBinDataOffset, $length);
|
||||||
$data = substr($bin, $this->readBinDataOffset, $length);
|
$this->readBinDataOffset += $length;
|
||||||
$this->readBinDataOffset += $length;
|
|
||||||
|
|
||||||
return bindec($data);
|
return bindec($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function bitrateLookup($index)
|
public static function bitrateLookup($index) {
|
||||||
{
|
static $lookup = array(
|
||||||
static $lookup = [
|
0 => 32000,
|
||||||
0 => 32000,
|
1 => 56000,
|
||||||
1 => 56000,
|
2 => 64000,
|
||||||
2 => 64000,
|
3 => 96000,
|
||||||
3 => 96000,
|
4 => 112000,
|
||||||
4 => 112000,
|
5 => 128000,
|
||||||
5 => 128000,
|
6 => 192000,
|
||||||
6 => 192000,
|
7 => 224000,
|
||||||
7 => 224000,
|
8 => 256000,
|
||||||
8 => 256000,
|
9 => 320000,
|
||||||
9 => 320000,
|
10 => 384000,
|
||||||
10 => 384000,
|
11 => 448000,
|
||||||
11 => 448000,
|
12 => 512000,
|
||||||
12 => 512000,
|
13 => 576000,
|
||||||
13 => 576000,
|
14 => 640000,
|
||||||
14 => 640000,
|
15 => 768000,
|
||||||
15 => 768000,
|
16 => 960000,
|
||||||
16 => 960000,
|
17 => 1024000,
|
||||||
17 => 1024000,
|
18 => 1152000,
|
||||||
18 => 1152000,
|
19 => 1280000,
|
||||||
19 => 1280000,
|
20 => 1344000,
|
||||||
20 => 1344000,
|
21 => 1408000,
|
||||||
21 => 1408000,
|
22 => 1411200,
|
||||||
22 => 1411200,
|
23 => 1472000,
|
||||||
23 => 1472000,
|
24 => 1536000,
|
||||||
24 => 1536000,
|
25 => 1920000,
|
||||||
25 => 1920000,
|
26 => 2048000,
|
||||||
26 => 2048000,
|
27 => 3072000,
|
||||||
27 => 3072000,
|
28 => 3840000,
|
||||||
28 => 3840000,
|
29 => 'open',
|
||||||
29 => 'open',
|
30 => 'variable',
|
||||||
30 => 'variable',
|
31 => 'lossless',
|
||||||
31 => 'lossless',
|
);
|
||||||
];
|
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
||||||
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function sampleRateLookup($index)
|
public static function sampleRateLookup($index) {
|
||||||
{
|
static $lookup = array(
|
||||||
static $lookup = [
|
0 => 'invalid',
|
||||||
0 => 'invalid',
|
1 => 8000,
|
||||||
1 => 8000,
|
2 => 16000,
|
||||||
2 => 16000,
|
3 => 32000,
|
||||||
3 => 32000,
|
4 => 'invalid',
|
||||||
4 => 'invalid',
|
5 => 'invalid',
|
||||||
5 => 'invalid',
|
6 => 11025,
|
||||||
6 => 11025,
|
7 => 22050,
|
||||||
7 => 22050,
|
8 => 44100,
|
||||||
8 => 44100,
|
9 => 'invalid',
|
||||||
9 => 'invalid',
|
10 => 'invalid',
|
||||||
10 => 'invalid',
|
11 => 12000,
|
||||||
11 => 12000,
|
12 => 24000,
|
||||||
12 => 24000,
|
13 => 48000,
|
||||||
13 => 48000,
|
14 => 'invalid',
|
||||||
14 => 'invalid',
|
15 => 'invalid',
|
||||||
15 => 'invalid',
|
);
|
||||||
];
|
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
||||||
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function bitPerSampleLookup($index)
|
public static function bitPerSampleLookup($index) {
|
||||||
{
|
static $lookup = array(
|
||||||
static $lookup = [
|
0 => 16,
|
||||||
0 => 16,
|
1 => 20,
|
||||||
1 => 20,
|
2 => 24,
|
||||||
2 => 24,
|
3 => 24,
|
||||||
3 => 24,
|
);
|
||||||
];
|
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
||||||
return (isset($lookup[$index]) ? $lookup[$index] : false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function numChannelsLookup($index)
|
public static function numChannelsLookup($index) {
|
||||||
{
|
switch ($index) {
|
||||||
switch ($index) {
|
case 0:
|
||||||
case 0:
|
return 1;
|
||||||
return 1;
|
break;
|
||||||
break;
|
case 1:
|
||||||
case 1:
|
case 2:
|
||||||
case 2:
|
case 3:
|
||||||
case 3:
|
case 4:
|
||||||
case 4:
|
return 2;
|
||||||
return 2;
|
break;
|
||||||
break;
|
case 5:
|
||||||
case 5:
|
case 6:
|
||||||
case 6:
|
return 3;
|
||||||
return 3;
|
break;
|
||||||
break;
|
case 7:
|
||||||
case 7:
|
case 8:
|
||||||
case 8:
|
return 4;
|
||||||
return 4;
|
break;
|
||||||
break;
|
case 9:
|
||||||
case 9:
|
return 5;
|
||||||
return 5;
|
break;
|
||||||
break;
|
case 10:
|
||||||
case 10:
|
case 11:
|
||||||
case 11:
|
case 12:
|
||||||
case 12:
|
return 6;
|
||||||
return 6;
|
break;
|
||||||
break;
|
case 13:
|
||||||
case 13:
|
return 7;
|
||||||
return 7;
|
break;
|
||||||
break;
|
case 14:
|
||||||
case 14:
|
case 15:
|
||||||
case 15:
|
return 8;
|
||||||
return 8;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static function channelArrangementLookup($index)
|
public static function channelArrangementLookup($index) {
|
||||||
{
|
static $lookup = array(
|
||||||
static $lookup = [
|
0 => 'A',
|
||||||
0 => 'A',
|
1 => 'A + B (dual mono)',
|
||||||
1 => 'A + B (dual mono)',
|
2 => 'L + R (stereo)',
|
||||||
2 => 'L + R (stereo)',
|
3 => '(L+R) + (L-R) (sum-difference)',
|
||||||
3 => '(L+R) + (L-R) (sum-difference)',
|
4 => 'LT + RT (left and right total)',
|
||||||
4 => 'LT + RT (left and right total)',
|
5 => 'C + L + R',
|
||||||
5 => 'C + L + R',
|
6 => 'L + R + S',
|
||||||
6 => 'L + R + S',
|
7 => 'C + L + R + S',
|
||||||
7 => 'C + L + R + S',
|
8 => 'L + R + SL + SR',
|
||||||
8 => 'L + R + SL + SR',
|
9 => 'C + L + R + SL + SR',
|
||||||
9 => 'C + L + R + SL + SR',
|
10 => 'CL + CR + L + R + SL + SR',
|
||||||
10 => 'CL + CR + L + R + SL + SR',
|
11 => 'C + L + R+ LR + RR + OV',
|
||||||
11 => 'C + L + R+ LR + RR + OV',
|
12 => 'CF + CR + LF + RF + LR + RR',
|
||||||
12 => 'CF + CR + LF + RF + LR + RR',
|
13 => 'CL + C + CR + L + R + SL + SR',
|
||||||
13 => 'CL + C + CR + L + R + SL + SR',
|
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 (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
|
||||||
return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
|
}
|
||||||
}
|
|
||||||
|
public static function dialogNormalization($index, $version) {
|
||||||
|
switch ($version) {
|
||||||
|
case 7:
|
||||||
|
return 0 - $index;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
return 0 - 16 - $index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static function dialogNormalization($index, $version)
|
|
||||||
{
|
|
||||||
switch ($version) {
|
|
||||||
case 7:
|
|
||||||
return 0 - $index;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
return 0 - 16 - $index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,433 +22,432 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE_
|
||||||
*/
|
*/
|
||||||
class getid3_flac extends getid3_handler
|
class getid3_flac extends getid3_handler
|
||||||
{
|
{
|
||||||
const syncword = 'fLaC';
|
const syncword = 'fLaC';
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$StreamMarker = $this->fread(4);
|
$StreamMarker = $this->fread(4);
|
||||||
if ($StreamMarker != self::syncword) {
|
if ($StreamMarker != self::syncword) {
|
||||||
return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
|
return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'flac';
|
$info['fileformat'] = 'flac';
|
||||||
$info['audio']['dataformat'] = 'flac';
|
$info['audio']['dataformat'] = 'flac';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
|
|
||||||
// parse flac container
|
// parse flac container
|
||||||
return $this->parseMETAdata();
|
return $this->parseMETAdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseMETAdata()
|
public function parseMETAdata() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
do {
|
||||||
do {
|
$BlockOffset = $this->ftell();
|
||||||
$BlockOffset = $this->ftell();
|
$BlockHeader = $this->fread(4);
|
||||||
$BlockHeader = $this->fread(4);
|
$LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
|
||||||
$LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
|
$LastBlockFlag = (bool) ($LBFBT & 0x80);
|
||||||
$LastBlockFlag = (bool) ($LBFBT & 0x80);
|
$BlockType = ($LBFBT & 0x7F);
|
||||||
$BlockType = ($LBFBT & 0x7F);
|
$BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
|
||||||
$BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
|
$BlockTypeText = self::metaBlockTypeLookup($BlockType);
|
||||||
$BlockTypeText = self::metaBlockTypeLookup($BlockType);
|
|
||||||
|
|
||||||
if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
|
if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
|
||||||
$this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
|
$this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($BlockLength < 1) {
|
if ($BlockLength < 1) {
|
||||||
$this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
|
$this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['flac'][$BlockTypeText]['raw'] = [];
|
$info['flac'][$BlockTypeText]['raw'] = array();
|
||||||
$BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
|
$BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
|
||||||
|
|
||||||
$BlockTypeText_raw['offset'] = $BlockOffset;
|
$BlockTypeText_raw['offset'] = $BlockOffset;
|
||||||
$BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
|
$BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
|
||||||
$BlockTypeText_raw['block_type'] = $BlockType;
|
$BlockTypeText_raw['block_type'] = $BlockType;
|
||||||
$BlockTypeText_raw['block_type_text'] = $BlockTypeText;
|
$BlockTypeText_raw['block_type_text'] = $BlockTypeText;
|
||||||
$BlockTypeText_raw['block_length'] = $BlockLength;
|
$BlockTypeText_raw['block_length'] = $BlockLength;
|
||||||
if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
|
if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
|
||||||
$BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
|
$BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($BlockTypeText) {
|
switch ($BlockTypeText) {
|
||||||
case 'STREAMINFO': // 0x00
|
case 'STREAMINFO': // 0x00
|
||||||
if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
|
if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'PADDING': // 0x01
|
case 'PADDING': // 0x01
|
||||||
unset($info['flac']['PADDING']); // ignore
|
unset($info['flac']['PADDING']); // ignore
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'APPLICATION': // 0x02
|
case 'APPLICATION': // 0x02
|
||||||
if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
|
if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'SEEKTABLE': // 0x03
|
case 'SEEKTABLE': // 0x03
|
||||||
if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
|
if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'VORBIS_COMMENT': // 0x04
|
case 'VORBIS_COMMENT': // 0x04
|
||||||
if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
|
if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'CUESHEET': // 0x05
|
case 'CUESHEET': // 0x05
|
||||||
if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
|
if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'PICTURE': // 0x06
|
case 'PICTURE': // 0x06
|
||||||
if (!$this->parsePICTURE()) {
|
if (!$this->parsePICTURE()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
|
$this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($info['flac'][$BlockTypeText]['raw']);
|
unset($info['flac'][$BlockTypeText]['raw']);
|
||||||
$info['avdataoffset'] = $this->ftell();
|
$info['avdataoffset'] = $this->ftell();
|
||||||
} while ($LastBlockFlag === false);
|
}
|
||||||
|
while ($LastBlockFlag === false);
|
||||||
|
|
||||||
// handle tags
|
// handle tags
|
||||||
if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
|
if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
|
||||||
$info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
|
$info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
|
||||||
}
|
}
|
||||||
if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
|
if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
|
||||||
$info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
|
$info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy attachments to 'comments' array if nesesary
|
// copy attachments to 'comments' array if nesesary
|
||||||
if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
|
if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
|
||||||
foreach ($info['flac']['PICTURE'] as $entry) {
|
foreach ($info['flac']['PICTURE'] as $entry) {
|
||||||
if (!empty($entry['data'])) {
|
if (!empty($entry['data'])) {
|
||||||
if (!isset($info['flac']['comments']['picture'])) {
|
if (!isset($info['flac']['comments']['picture'])) {
|
||||||
$info['flac']['comments']['picture'] = [];
|
$info['flac']['comments']['picture'] = array();
|
||||||
}
|
}
|
||||||
$comments_picture_data = [];
|
$comments_picture_data = array();
|
||||||
foreach (['data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength'] as $picture_key) {
|
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
|
||||||
if (isset($entry[$picture_key])) {
|
if (isset($entry[$picture_key])) {
|
||||||
$comments_picture_data[$picture_key] = $entry[$picture_key];
|
$comments_picture_data[$picture_key] = $entry[$picture_key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$info['flac']['comments']['picture'][] = $comments_picture_data;
|
$info['flac']['comments']['picture'][] = $comments_picture_data;
|
||||||
unset($comments_picture_data);
|
unset($comments_picture_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($info['flac']['STREAMINFO'])) {
|
if (isset($info['flac']['STREAMINFO'])) {
|
||||||
if (!$this->isDependencyFor('matroska')) {
|
if (!$this->isDependencyFor('matroska')) {
|
||||||
$info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
|
$info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
|
||||||
}
|
}
|
||||||
$info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
|
$info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
|
||||||
if ($info['flac']['uncompressed_audio_bytes'] == 0) {
|
if ($info['flac']['uncompressed_audio_bytes'] == 0) {
|
||||||
return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
|
return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
|
||||||
}
|
}
|
||||||
if (!empty($info['flac']['compressed_audio_bytes'])) {
|
if (!empty($info['flac']['compressed_audio_bytes'])) {
|
||||||
$info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
|
$info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set md5_data_source - built into flac 0.5+
|
// set md5_data_source - built into flac 0.5+
|
||||||
if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
|
if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
|
||||||
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
|
|
||||||
|
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
|
||||||
$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
|
$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
|
||||||
} else {
|
}
|
||||||
$info['md5_data_source'] = '';
|
else {
|
||||||
$md5 = $info['flac']['STREAMINFO']['audio_signature'];
|
$info['md5_data_source'] = '';
|
||||||
for ($i = 0; $i < strlen($md5); $i++) {
|
$md5 = $info['flac']['STREAMINFO']['audio_signature'];
|
||||||
$info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
|
for ($i = 0; $i < strlen($md5); $i++) {
|
||||||
}
|
$info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
|
||||||
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
|
}
|
||||||
unset($info['md5_data_source']);
|
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
|
||||||
}
|
unset($info['md5_data_source']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
|
if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
|
||||||
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
|
||||||
if ($info['audio']['bits_per_sample'] == 8) {
|
if ($info['audio']['bits_per_sample'] == 8) {
|
||||||
// special case
|
// special case
|
||||||
// must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
|
// 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
|
// MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
|
||||||
$this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
|
$this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseSTREAMINFO($BlockData)
|
private function parseSTREAMINFO($BlockData) {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['flac']['STREAMINFO'] = [];
|
$info['flac']['STREAMINFO'] = array();
|
||||||
$streaminfo = &$info['flac']['STREAMINFO'];
|
$streaminfo = &$info['flac']['STREAMINFO'];
|
||||||
|
|
||||||
$streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
|
$streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
|
||||||
$streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
|
$streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
|
||||||
$streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
|
$streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
|
||||||
$streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
|
$streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
|
||||||
|
|
||||||
$SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
|
$SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
|
||||||
$streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
|
$streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
|
||||||
$streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
|
$streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
|
||||||
$streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
|
$streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
|
||||||
$streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
|
$streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
|
||||||
|
|
||||||
$streaminfo['audio_signature'] = substr($BlockData, 18, 16);
|
$streaminfo['audio_signature'] = substr($BlockData, 18, 16);
|
||||||
|
|
||||||
if (!empty($streaminfo['sample_rate'])) {
|
if (!empty($streaminfo['sample_rate'])) {
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
|
||||||
$info['audio']['sample_rate'] = $streaminfo['sample_rate'];
|
|
||||||
$info['audio']['channels'] = $streaminfo['channels'];
|
|
||||||
$info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
|
|
||||||
$info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
|
|
||||||
if ($info['playtime_seconds'] > 0) {
|
|
||||||
if (!$this->isDependencyFor('matroska')) {
|
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
|
||||||
} else {
|
|
||||||
$this->warning('Cannot determine audio bitrate because total stream size is unknown');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return $this->error('Corrupt METAdata block: STREAMINFO');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
}
|
$info['audio']['sample_rate'] = $streaminfo['sample_rate'];
|
||||||
|
$info['audio']['channels'] = $streaminfo['channels'];
|
||||||
|
$info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
|
||||||
|
$info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
|
||||||
|
if ($info['playtime_seconds'] > 0) {
|
||||||
|
if (!$this->isDependencyFor('matroska')) {
|
||||||
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->warning('Cannot determine audio bitrate because total stream size is unknown');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function parseAPPLICATION($BlockData)
|
} else {
|
||||||
{
|
return $this->error('Corrupt METAdata block: STREAMINFO');
|
||||||
$info = &$this->getid3->info;
|
}
|
||||||
|
|
||||||
$ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
|
return true;
|
||||||
$info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
|
}
|
||||||
$info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
|
|
||||||
|
|
||||||
return true;
|
private function parseAPPLICATION($BlockData) {
|
||||||
}
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
private function parseSEEKTABLE($BlockData)
|
$ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
|
||||||
{
|
$info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
|
||||||
$info = &$this->getid3->info;
|
$info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
|
||||||
|
|
||||||
$offset = 0;
|
return true;
|
||||||
$BlockLength = strlen($BlockData);
|
}
|
||||||
$placeholderpattern = str_repeat("\xFF", 8);
|
|
||||||
while ($offset < $BlockLength) {
|
|
||||||
$SampleNumberString = substr($BlockData, $offset, 8);
|
|
||||||
$offset += 8;
|
|
||||||
if ($SampleNumberString == $placeholderpattern) {
|
|
||||||
// placeholder point
|
|
||||||
getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
|
|
||||||
$offset += 10;
|
|
||||||
} else {
|
|
||||||
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
|
|
||||||
$info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
|
||||||
$offset += 8;
|
|
||||||
$info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
|
|
||||||
$offset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
private function parseSEEKTABLE($BlockData) {
|
||||||
}
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
private function parseVORBIS_COMMENT($BlockData)
|
$offset = 0;
|
||||||
{
|
$BlockLength = strlen($BlockData);
|
||||||
$info = &$this->getid3->info;
|
$placeholderpattern = str_repeat("\xFF", 8);
|
||||||
|
while ($offset < $BlockLength) {
|
||||||
|
$SampleNumberString = substr($BlockData, $offset, 8);
|
||||||
|
$offset += 8;
|
||||||
|
if ($SampleNumberString == $placeholderpattern) {
|
||||||
|
|
||||||
$getid3_ogg = new getid3_ogg($this->getid3);
|
// placeholder point
|
||||||
if ($this->isDependencyFor('matroska')) {
|
getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
|
||||||
$getid3_ogg->setStringMode($this->data_string);
|
$offset += 10;
|
||||||
}
|
|
||||||
$getid3_ogg->ParseVorbisComments();
|
|
||||||
if (isset($info['ogg'])) {
|
|
||||||
unset($info['ogg']['comments_raw']);
|
|
||||||
$info['flac']['VORBIS_COMMENT'] = $info['ogg'];
|
|
||||||
unset($info['ogg']);
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($getid3_ogg);
|
} else {
|
||||||
|
|
||||||
return true;
|
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
|
||||||
}
|
$info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
||||||
|
$offset += 8;
|
||||||
|
$info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
|
||||||
|
$offset += 2;
|
||||||
|
|
||||||
private function parseCUESHEET($BlockData)
|
}
|
||||||
{
|
}
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$offset = 0;
|
|
||||||
$info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
|
|
||||||
$offset += 128;
|
|
||||||
$info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
|
||||||
$offset += 8;
|
|
||||||
$info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
$offset += 258; // reserved
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
private function parseVORBIS_COMMENT($BlockData) {
|
||||||
$offset += 1;
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
|
$getid3_ogg = new getid3_ogg($this->getid3);
|
||||||
$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
if ($this->isDependencyFor('matroska')) {
|
||||||
$offset += 8;
|
$getid3_ogg->setStringMode($this->data_string);
|
||||||
$TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
}
|
||||||
$offset += 1;
|
$getid3_ogg->ParseVorbisComments();
|
||||||
|
if (isset($info['ogg'])) {
|
||||||
|
unset($info['ogg']['comments_raw']);
|
||||||
|
$info['flac']['VORBIS_COMMENT'] = $info['ogg'];
|
||||||
|
unset($info['ogg']);
|
||||||
|
}
|
||||||
|
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
|
unset($getid3_ogg);
|
||||||
|
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
|
return true;
|
||||||
$offset += 12;
|
}
|
||||||
|
|
||||||
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
private function parseCUESHEET($BlockData) {
|
||||||
$offset += 1;
|
$info = &$this->getid3->info;
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
|
$offset = 0;
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
|
$info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
|
||||||
|
$offset += 128;
|
||||||
|
$info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
||||||
|
$offset += 8;
|
||||||
|
$info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
|
||||||
|
$offset += 1;
|
||||||
|
|
||||||
$offset += 13; // reserved
|
$offset += 258; // reserved
|
||||||
|
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
$info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
|
|
||||||
for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
|
for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
|
||||||
$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
||||||
$offset += 8;
|
$offset += 8;
|
||||||
$IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
$TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
|
|
||||||
$offset += 3; // reserved
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
|
||||||
|
|
||||||
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
|
||||||
}
|
$offset += 12;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
||||||
}
|
$offset += 1;
|
||||||
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
|
||||||
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
|
||||||
|
|
||||||
/**
|
$offset += 13; // reserved
|
||||||
* Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
|
|
||||||
* External usage: audio.ogg
|
|
||||||
*/
|
|
||||||
public function parsePICTURE()
|
|
||||||
{
|
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
|
$offset += 1;
|
||||||
$picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
|
|
||||||
$descr_length = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
if ($descr_length) {
|
|
||||||
$picture['description'] = $this->fread($descr_length);
|
|
||||||
}
|
|
||||||
$picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
$picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
$picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
$picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
$picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
|
|
||||||
|
|
||||||
if ($picture['image_mime'] == '-->') {
|
for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
|
||||||
$picture['data'] = $this->fread($picture['datalength']);
|
$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
|
||||||
} else {
|
$offset += 8;
|
||||||
$picture['data'] = $this->saveAttachment(
|
$IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
|
||||||
str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
|
$offset += 1;
|
||||||
$this->ftell(),
|
|
||||||
$picture['datalength'],
|
|
||||||
$picture['image_mime']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['flac']['PICTURE'][] = $picture;
|
$offset += 3; // reserved
|
||||||
|
|
||||||
return true;
|
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function metaBlockTypeLookup($blocktype)
|
return true;
|
||||||
{
|
}
|
||||||
static $lookup = [
|
|
||||||
0 => 'STREAMINFO',
|
|
||||||
1 => 'PADDING',
|
|
||||||
2 => 'APPLICATION',
|
|
||||||
3 => 'SEEKTABLE',
|
|
||||||
4 => 'VORBIS_COMMENT',
|
|
||||||
5 => 'CUESHEET',
|
|
||||||
6 => 'PICTURE',
|
|
||||||
];
|
|
||||||
return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function applicationIDLookup($applicationid)
|
/**
|
||||||
{
|
* Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
|
||||||
// http://flac.sourceforge.net/id.html
|
* External usage: audio.ogg
|
||||||
static $lookup = [
|
*/
|
||||||
0x41544348 => 'FlacFile', // "ATCH"
|
public function parsePICTURE() {
|
||||||
0x42534F4C => 'beSolo', // "BSOL"
|
$info = &$this->getid3->info;
|
||||||
0x42554753 => 'Bugs Player', // "BUGS"
|
|
||||||
0x43756573 => 'GoldWave cue points (specification)', // "Cues"
|
$picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x46696361 => 'CUE Splitter', // "Fica"
|
$picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
|
||||||
0x46746F6C => 'flac-tools', // "Ftol"
|
$picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
|
||||||
0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
|
$descr_length = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x4D505345 => 'MP3 Stream Editor', // "MPSE"
|
if ($descr_length) {
|
||||||
0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
|
$picture['description'] = $this->fread($descr_length);
|
||||||
0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
|
}
|
||||||
0x5346464C => 'Sound Font FLAC', // "SFFL"
|
$picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x534F4E59 => 'Sony Creative Software', // "SONY"
|
$picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x5351455A => 'flacsqueeze', // "SQEZ"
|
$picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x54745776 => 'TwistedWave', // "TtWv"
|
$picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x55495453 => 'UITS Embedding tools', // "UITS"
|
$picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
|
||||||
0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
|
|
||||||
0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
|
if ($picture['image_mime'] == '-->') {
|
||||||
0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
|
$picture['data'] = $this->fread($picture['datalength']);
|
||||||
0x71667374 => 'QFLAC Studio', // "qfst"
|
} else {
|
||||||
0x72696666 => 'FLAC RIFF chunk storage', // "riff"
|
$picture['data'] = $this->saveAttachment(
|
||||||
0x74756E65 => 'TagTuner', // "tune"
|
str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
|
||||||
0x78626174 => 'XBAT', // "xbat"
|
$this->ftell(),
|
||||||
0x786D6364 => 'xmcd', // "xmcd"
|
$picture['datalength'],
|
||||||
];
|
$picture['image_mime']);
|
||||||
return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
|
}
|
||||||
}
|
|
||||||
|
$info['flac']['PICTURE'][] = $picture;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function metaBlockTypeLookup($blocktype) {
|
||||||
|
static $lookup = array(
|
||||||
|
0 => 'STREAMINFO',
|
||||||
|
1 => 'PADDING',
|
||||||
|
2 => 'APPLICATION',
|
||||||
|
3 => 'SEEKTABLE',
|
||||||
|
4 => 'VORBIS_COMMENT',
|
||||||
|
5 => 'CUESHEET',
|
||||||
|
6 => 'PICTURE',
|
||||||
|
);
|
||||||
|
return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function applicationIDLookup($applicationid) {
|
||||||
|
// http://flac.sourceforge.net/id.html
|
||||||
|
static $lookup = array(
|
||||||
|
0x41544348 => 'FlacFile', // "ATCH"
|
||||||
|
0x42534F4C => 'beSolo', // "BSOL"
|
||||||
|
0x42554753 => 'Bugs Player', // "BUGS"
|
||||||
|
0x43756573 => 'GoldWave cue points (specification)', // "Cues"
|
||||||
|
0x46696361 => 'CUE Splitter', // "Fica"
|
||||||
|
0x46746F6C => 'flac-tools', // "Ftol"
|
||||||
|
0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
|
||||||
|
0x4D505345 => 'MP3 Stream Editor', // "MPSE"
|
||||||
|
0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
|
||||||
|
0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
|
||||||
|
0x5346464C => 'Sound Font FLAC', // "SFFL"
|
||||||
|
0x534F4E59 => 'Sony Creative Software', // "SONY"
|
||||||
|
0x5351455A => 'flacsqueeze', // "SQEZ"
|
||||||
|
0x54745776 => 'TwistedWave', // "TtWv"
|
||||||
|
0x55495453 => 'UITS Embedding tools', // "UITS"
|
||||||
|
0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
|
||||||
|
0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
|
||||||
|
0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
|
||||||
|
0x71667374 => 'QFLAC Studio', // "qfst"
|
||||||
|
0x72696666 => 'FLAC RIFF chunk storage', // "riff"
|
||||||
|
0x74756E65 => 'TagTuner', // "tune"
|
||||||
|
0x78626174 => 'XBAT', // "xbat"
|
||||||
|
0x786D6364 => 'xmcd', // "xmcd"
|
||||||
|
);
|
||||||
|
return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pictureTypeLookup($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');
|
||||||
|
}
|
||||||
|
|
||||||
public static function pictureTypeLookup($type_id)
|
|
||||||
{
|
|
||||||
static $lookup = [
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,200 +19,208 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
|
||||||
class getid3_la extends getid3_handler
|
class getid3_la extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$rawdata = $this->fread($this->getid3->fread_buffer_size());
|
$rawdata = $this->fread($this->getid3->fread_buffer_size());
|
||||||
|
|
||||||
switch (substr($rawdata, $offset, 4)) {
|
switch (substr($rawdata, $offset, 4)) {
|
||||||
case 'LA02':
|
case 'LA02':
|
||||||
case 'LA03':
|
case 'LA03':
|
||||||
case 'LA04':
|
case 'LA04':
|
||||||
$info['fileformat'] = 'la';
|
$info['fileformat'] = 'la';
|
||||||
$info['audio']['dataformat'] = 'la';
|
$info['audio']['dataformat'] = 'la';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
|
|
||||||
$info['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
|
$info['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
|
||||||
$info['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
|
$info['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
|
||||||
$info['la']['version'] = (float) $info['la']['version_major'] + ($info['la']['version_minor'] / 10);
|
$info['la']['version'] = (float) $info['la']['version_major'] + ($info['la']['version_minor'] / 10);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
$info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
$info['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
if ($info['la']['uncompressed_size'] == 0) {
|
if ($info['la']['uncompressed_size'] == 0) {
|
||||||
$info['error'][] = 'Corrupt LA file: uncompressed_size == zero';
|
$this->error('Corrupt LA file: uncompressed_size == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$WAVEchunk = substr($rawdata, $offset, 4);
|
$WAVEchunk = substr($rawdata, $offset, 4);
|
||||||
if ($WAVEchunk !== 'WAVE') {
|
if ($WAVEchunk !== 'WAVE') {
|
||||||
$info['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
|
$this->error('Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
$info['la']['fmt_size'] = 24;
|
$info['la']['fmt_size'] = 24;
|
||||||
if ($info['la']['version'] >= 0.3) {
|
if ($info['la']['version'] >= 0.3) {
|
||||||
$info['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
|
||||||
$info['la']['header_size'] = 49 + $info['la']['fmt_size'] - 24;
|
|
||||||
$offset += 4;
|
|
||||||
} else {
|
|
||||||
// version 0.2 didn't support additional data blocks
|
|
||||||
$info['la']['header_size'] = 41;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fmt_chunk = substr($rawdata, $offset, 4);
|
$info['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
if ($fmt_chunk !== 'fmt ') {
|
$info['la']['header_size'] = 49 + $info['la']['fmt_size'] - 24;
|
||||||
$info['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.';
|
$offset += 4;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$offset += 4;
|
|
||||||
$fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
|
||||||
$offset += 4;
|
|
||||||
|
|
||||||
$info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
} else {
|
||||||
$offset += 2;
|
|
||||||
|
|
||||||
$info['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
// version 0.2 didn't support additional data blocks
|
||||||
$offset += 2;
|
$info['la']['header_size'] = 41;
|
||||||
if ($info['la']['channels'] == 0) {
|
|
||||||
$info['error'][] = 'Corrupt LA file: channels == zero';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
}
|
||||||
$offset += 4;
|
|
||||||
if ($info['la']['sample_rate'] == 0) {
|
|
||||||
$info['error'][] = 'Corrupt LA file: sample_rate == zero';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
$fmt_chunk = substr($rawdata, $offset, 4);
|
||||||
$offset += 4;
|
if ($fmt_chunk !== 'fmt ') {
|
||||||
$info['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
$this->error('Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.');
|
||||||
$offset += 2;
|
return false;
|
||||||
$info['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
}
|
||||||
$offset += 2;
|
$offset += 4;
|
||||||
|
$fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
|
$offset += 4;
|
||||||
|
|
||||||
$info['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
$info['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||||
$offset += 4;
|
$offset += 2;
|
||||||
|
|
||||||
$info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
|
$info['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||||
$offset += 1;
|
$offset += 2;
|
||||||
$info['la']['flags']['seekable'] = (bool) ($info['la']['raw']['flags'] & 0x01);
|
if ($info['la']['channels'] == 0) {
|
||||||
if ($info['la']['version'] >= 0.4) {
|
$this->error('Corrupt LA file: channels == zero');
|
||||||
$info['la']['flags']['high_compression'] = (bool) ($info['la']['raw']['flags'] & 0x02);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
$info['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
if ($info['la']['sample_rate'] == 0) {
|
||||||
|
$this->error('Corrupt LA file: sample_rate == zero');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// mikeØbevin*de
|
$info['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
|
$offset += 4;
|
||||||
// in earlier versions. A seekpoint is added every blocksize * seekevery
|
$info['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||||
// samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
|
$offset += 2;
|
||||||
// give the number of bytes used for the seekpoints. Of course, if seeking
|
$info['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
|
||||||
// is disabled, there are no seekpoints stored.
|
$offset += 2;
|
||||||
if ($info['la']['version'] >= 0.4) {
|
|
||||||
$info['la']['blocksize'] = 61440;
|
|
||||||
$info['la']['seekevery'] = 19;
|
|
||||||
} else {
|
|
||||||
$info['la']['blocksize'] = 73728;
|
|
||||||
$info['la']['seekevery'] = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['la']['seekpoint_count'] = 0;
|
$info['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
if ($info['la']['flags']['seekable']) {
|
$offset += 4;
|
||||||
$info['la']['seekpoint_count'] = floor($info['la']['samples'] / ($info['la']['blocksize'] * $info['la']['seekevery']));
|
|
||||||
|
|
||||||
for ($i = 0; $i < $info['la']['seekpoint_count']; $i++) {
|
$info['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
|
||||||
$info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
$offset += 1;
|
||||||
$offset += 4;
|
$info['la']['flags']['seekable'] = (bool) ($info['la']['raw']['flags'] & 0x01);
|
||||||
}
|
if ($info['la']['version'] >= 0.4) {
|
||||||
}
|
$info['la']['flags']['high_compression'] = (bool) ($info['la']['raw']['flags'] & 0x02);
|
||||||
|
}
|
||||||
|
|
||||||
if ($info['la']['version'] >= 0.3) {
|
$info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
// Following the main header information, the program outputs all of the
|
$offset += 4;
|
||||||
// seekpoints. Following these is what I called the 'footer start',
|
|
||||||
// i.e. the position immediately after the La audio data is finished.
|
|
||||||
$info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
|
||||||
$offset += 4;
|
|
||||||
|
|
||||||
if ($info['la']['footerstart'] > $info['filesize']) {
|
// mikeØbevin*de
|
||||||
$info['warning'][] = 'FooterStart value points to offset '.$info['la']['footerstart'].' which is beyond end-of-file ('.$info['filesize'].')';
|
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
|
||||||
$info['la']['footerstart'] = $info['filesize'];
|
// in earlier versions. A seekpoint is added every blocksize * seekevery
|
||||||
}
|
// samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
|
||||||
} else {
|
// give the number of bytes used for the seekpoints. Of course, if seeking
|
||||||
// La v0.2 didn't have FooterStart value
|
// is disabled, there are no seekpoints stored.
|
||||||
$info['la']['footerstart'] = $info['avdataend'];
|
if ($info['la']['version'] >= 0.4) {
|
||||||
}
|
$info['la']['blocksize'] = 61440;
|
||||||
|
$info['la']['seekevery'] = 19;
|
||||||
|
} else {
|
||||||
|
$info['la']['blocksize'] = 73728;
|
||||||
|
$info['la']['seekevery'] = 16;
|
||||||
|
}
|
||||||
|
|
||||||
if ($info['la']['footerstart'] < $info['avdataend']) {
|
$info['la']['seekpoint_count'] = 0;
|
||||||
if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
|
if ($info['la']['flags']['seekable']) {
|
||||||
if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
|
$info['la']['seekpoint_count'] = floor($info['la']['samples'] / ($info['la']['blocksize'] * $info['la']['seekevery']));
|
||||||
$RIFFdata = 'WAVE';
|
|
||||||
if ($info['la']['version'] == 0.2) {
|
|
||||||
$RIFFdata .= substr($rawdata, 12, 24);
|
|
||||||
} else {
|
|
||||||
$RIFFdata .= substr($rawdata, 16, 24);
|
|
||||||
}
|
|
||||||
if ($info['la']['footerstart'] < $info['avdataend']) {
|
|
||||||
$this->fseek($info['la']['footerstart']);
|
|
||||||
$RIFFdata .= $this->fread($info['avdataend'] - $info['la']['footerstart']);
|
|
||||||
}
|
|
||||||
$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
|
|
||||||
fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
|
|
||||||
fclose($RIFF_fp);
|
|
||||||
|
|
||||||
$getid3_temp = new getID3();
|
for ($i = 0; $i < $info['la']['seekpoint_count']; $i++) {
|
||||||
$getid3_temp->openfile($RIFFtempfilename);
|
$info['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
$offset += 4;
|
||||||
$getid3_riff->Analyze();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($getid3_temp->info['error'])) {
|
if ($info['la']['version'] >= 0.3) {
|
||||||
$info['riff'] = $getid3_temp->info['riff'];
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'Error parsing RIFF portion of La file: '.implode($getid3_temp->info['error']);
|
|
||||||
}
|
|
||||||
unset($getid3_temp, $getid3_riff);
|
|
||||||
}
|
|
||||||
unlink($RIFFtempfilename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// $info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
|
// Following the main header information, the program outputs all of the
|
||||||
$info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart'];
|
// seekpoints. Following these is what I called the 'footer start',
|
||||||
$info['avdataoffset'] = $info['avdataoffset'] + $offset;
|
// i.e. the position immediately after the La audio data is finished.
|
||||||
|
$info['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
|
||||||
|
$offset += 4;
|
||||||
|
|
||||||
$info['la']['compression_ratio'] = (float) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']);
|
if ($info['la']['footerstart'] > $info['filesize']) {
|
||||||
$info['playtime_seconds'] = (float) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels'];
|
$this->warning('FooterStart value points to offset '.$info['la']['footerstart'].' which is beyond end-of-file ('.$info['filesize'].')');
|
||||||
if ($info['playtime_seconds'] == 0) {
|
$info['la']['footerstart'] = $info['filesize'];
|
||||||
$info['error'][] = 'Corrupt LA file: playtime_seconds == zero';
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
|
} else {
|
||||||
//$info['audio']['codec'] = $info['la']['codec'];
|
|
||||||
$info['audio']['bits_per_sample'] = $info['la']['bits_per_sample'];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
// La v0.2 didn't have FooterStart value
|
||||||
if (substr($rawdata, $offset, 2) == 'LA') {
|
$info['la']['footerstart'] = $info['avdataend'];
|
||||||
$info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] does not support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.';
|
|
||||||
} else {
|
|
||||||
$info['error'][] = 'Not a LA (Lossless-Audio) file';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['audio']['channels'] = $info['la']['channels'];
|
}
|
||||||
$info['audio']['sample_rate'] = (int) $info['la']['sample_rate'];
|
|
||||||
$info['audio']['encoder'] = 'LA v'.$info['la']['version'];
|
if ($info['la']['footerstart'] < $info['avdataend']) {
|
||||||
|
if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
|
||||||
|
if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
|
||||||
|
$RIFFdata = 'WAVE';
|
||||||
|
if ($info['la']['version'] == 0.2) {
|
||||||
|
$RIFFdata .= substr($rawdata, 12, 24);
|
||||||
|
} else {
|
||||||
|
$RIFFdata .= substr($rawdata, 16, 24);
|
||||||
|
}
|
||||||
|
if ($info['la']['footerstart'] < $info['avdataend']) {
|
||||||
|
$this->fseek($info['la']['footerstart']);
|
||||||
|
$RIFFdata .= $this->fread($info['avdataend'] - $info['la']['footerstart']);
|
||||||
|
}
|
||||||
|
$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
|
||||||
|
fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
|
||||||
|
fclose($RIFF_fp);
|
||||||
|
|
||||||
|
$getid3_temp = new getID3();
|
||||||
|
$getid3_temp->openfile($RIFFtempfilename);
|
||||||
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
|
$getid3_riff->Analyze();
|
||||||
|
|
||||||
|
if (empty($getid3_temp->info['error'])) {
|
||||||
|
$info['riff'] = $getid3_temp->info['riff'];
|
||||||
|
} else {
|
||||||
|
$this->warning('Error parsing RIFF portion of La file: '.implode($getid3_temp->info['error']));
|
||||||
|
}
|
||||||
|
unset($getid3_temp, $getid3_riff);
|
||||||
|
}
|
||||||
|
unlink($RIFFtempfilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
|
||||||
|
$info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart'];
|
||||||
|
$info['avdataoffset'] = $info['avdataoffset'] + $offset;
|
||||||
|
|
||||||
|
$info['la']['compression_ratio'] = (float) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']);
|
||||||
|
$info['playtime_seconds'] = (float) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels'];
|
||||||
|
if ($info['playtime_seconds'] == 0) {
|
||||||
|
$this->error('Corrupt LA file: playtime_seconds == zero');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
|
||||||
|
//$info['audio']['codec'] = $info['la']['codec'];
|
||||||
|
$info['audio']['bits_per_sample'] = $info['la']['bits_per_sample'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (substr($rawdata, $offset, 2) == 'LA') {
|
||||||
|
$this->error('This version of getID3() ['.$this->getid3->version().'] does not support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.');
|
||||||
|
} else {
|
||||||
|
$this->error('Not a LA (Lossless-Audio) file');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info['audio']['channels'] = $info['la']['channels'];
|
||||||
|
$info['audio']['sample_rate'] = (int) $info['la']['sample_rate'];
|
||||||
|
$info['audio']['encoder'] = 'LA v'.$info['la']['version'];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,110 +19,110 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
|
||||||
class getid3_lpac extends getid3_handler
|
class getid3_lpac extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$LPACheader = $this->fread(14);
|
$LPACheader = $this->fread(14);
|
||||||
if (substr($LPACheader, 0, 4) != 'LPAC') {
|
if (substr($LPACheader, 0, 4) != 'LPAC') {
|
||||||
$info['error'][] = 'Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"';
|
$this->error('Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['avdataoffset'] += 14;
|
$info['avdataoffset'] += 14;
|
||||||
|
|
||||||
$info['fileformat'] = 'lpac';
|
$info['fileformat'] = 'lpac';
|
||||||
$info['audio']['dataformat'] = 'lpac';
|
$info['audio']['dataformat'] = 'lpac';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
|
||||||
$info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
|
$info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
|
||||||
$flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
|
$flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
|
||||||
$info['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
|
$info['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
|
||||||
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
|
$flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
|
||||||
|
|
||||||
$info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
|
$info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
|
||||||
$info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04);
|
$info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04);
|
||||||
$info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02);
|
$info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02);
|
||||||
$info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01);
|
$info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01);
|
||||||
|
|
||||||
if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
|
if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
|
||||||
$info['warning'][] = '24-bit and 16-bit flags cannot both be set';
|
$this->warning('24-bit and 16-bit flags cannot both be set');
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
|
$info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
|
||||||
$info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
|
$info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000);
|
||||||
$info['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
|
$info['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
|
||||||
$info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
|
$info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000);
|
||||||
$info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
|
$info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000);
|
||||||
$info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
|
$info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000);
|
||||||
$info['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
|
$info['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8;
|
||||||
$info['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
|
$info['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F);
|
||||||
|
|
||||||
if ($info['lpac']['flags']['fast_compress'] && ($info['lpac']['max_prediction_order'] != 3)) {
|
if ($info['lpac']['flags']['fast_compress'] && ($info['lpac']['max_prediction_order'] != 3)) {
|
||||||
$info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info['lpac']['max_prediction_order'].'"';
|
$this->warning('max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info['lpac']['max_prediction_order'].'"');
|
||||||
}
|
}
|
||||||
switch ($info['lpac']['file_version']) {
|
switch ($info['lpac']['file_version']) {
|
||||||
case 6:
|
case 6:
|
||||||
if ($info['lpac']['flags']['adaptive_quantization']) {
|
if ($info['lpac']['flags']['adaptive_quantization']) {
|
||||||
$info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
|
$this->warning('adaptive_quantization expected to be false in LPAC file stucture v6, actually true');
|
||||||
}
|
}
|
||||||
if ($info['lpac']['quantization'] != 20) {
|
if ($info['lpac']['quantization'] != 20) {
|
||||||
$info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$info['lpac']['flags']['Q'];
|
$this->warning('Quantization expected to be 20 in LPAC file stucture v6, actually '.$info['lpac']['flags']['Q']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org';
|
//$this->warning('This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$getid3_temp = new getID3();
|
$getid3_temp = new getID3();
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
$getid3_temp->info = $info;
|
$getid3_temp->info = $info;
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
$getid3_riff->Analyze();
|
$getid3_riff->Analyze();
|
||||||
$info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
|
$info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
|
||||||
$info['riff'] = $getid3_temp->info['riff'];
|
$info['riff'] = $getid3_temp->info['riff'];
|
||||||
$info['error'] = $getid3_temp->info['error'];
|
$info['error'] = $getid3_temp->info['error'];
|
||||||
$info['warning'] = $getid3_temp->info['warning'];
|
$info['warning'] = $getid3_temp->info['warning'];
|
||||||
$info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
|
$info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
|
||||||
$info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
|
$info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
|
||||||
unset($getid3_temp, $getid3_riff);
|
unset($getid3_temp, $getid3_riff);
|
||||||
|
|
||||||
$info['audio']['channels'] = ($info['lpac']['flags']['stereo'] ? 2 : 1);
|
$info['audio']['channels'] = ($info['lpac']['flags']['stereo'] ? 2 : 1);
|
||||||
|
|
||||||
if ($info['lpac']['flags']['24_bit']) {
|
if ($info['lpac']['flags']['24_bit']) {
|
||||||
$info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
|
||||||
} elseif ($info['lpac']['flags']['16_bit']) {
|
} elseif ($info['lpac']['flags']['16_bit']) {
|
||||||
$info['audio']['bits_per_sample'] = 16;
|
$info['audio']['bits_per_sample'] = 16;
|
||||||
} else {
|
} else {
|
||||||
$info['audio']['bits_per_sample'] = 8;
|
$info['audio']['bits_per_sample'] = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($info['lpac']['flags']['fast_compress']) {
|
if ($info['lpac']['flags']['fast_compress']) {
|
||||||
// fast
|
// fast
|
||||||
$info['audio']['encoder_options'] = '-1';
|
$info['audio']['encoder_options'] = '-1';
|
||||||
} else {
|
} else {
|
||||||
switch ($info['lpac']['max_prediction_order']) {
|
switch ($info['lpac']['max_prediction_order']) {
|
||||||
case 20: // simple
|
case 20: // simple
|
||||||
$info['audio']['encoder_options'] = '-2';
|
$info['audio']['encoder_options'] = '-2';
|
||||||
break;
|
break;
|
||||||
case 30: // medium
|
case 30: // medium
|
||||||
$info['audio']['encoder_options'] = '-3';
|
$info['audio']['encoder_options'] = '-3';
|
||||||
break;
|
break;
|
||||||
case 40: // high
|
case 40: // high
|
||||||
$info['audio']['encoder_options'] = '-4';
|
$info['audio']['encoder_options'] = '-4';
|
||||||
break;
|
break;
|
||||||
case 60: // extrahigh
|
case 60: // extrahigh
|
||||||
$info['audio']['encoder_options'] = '-5';
|
$info['audio']['encoder_options'] = '-5';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
|
$info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,499 +19,512 @@ define('GETID3_MIDI_MAGIC_MTRK', 'MTrk'); // MIDI track header magic
|
||||||
|
|
||||||
class getid3_midi extends getid3_handler
|
class getid3_midi extends getid3_handler
|
||||||
{
|
{
|
||||||
public $scanwholefile = true;
|
public $scanwholefile = true;
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['midi']['raw'] = [];
|
$info['midi']['raw'] = array();
|
||||||
$thisfile_midi = &$info['midi'];
|
$thisfile_midi = &$info['midi'];
|
||||||
$thisfile_midi_raw = &$thisfile_midi['raw'];
|
$thisfile_midi_raw = &$thisfile_midi['raw'];
|
||||||
|
|
||||||
$info['fileformat'] = 'midi';
|
$info['fileformat'] = 'midi';
|
||||||
$info['audio']['dataformat'] = 'midi';
|
$info['audio']['dataformat'] = 'midi';
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$MIDIdata = $this->fread($this->getid3->fread_buffer_size());
|
$MIDIdata = $this->fread($this->getid3->fread_buffer_size());
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd'
|
$MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd'
|
||||||
if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) {
|
if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTHD).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($MIDIheaderID).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTHD).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($MIDIheaderID).'"');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
$thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
$thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
$thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
$thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
|
|
||||||
for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
|
for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
|
||||||
while ((strlen($MIDIdata) - $offset) < 8) {
|
while ((strlen($MIDIdata) - $offset) < 8) {
|
||||||
if ($buffer = $this->fread($this->getid3->fread_buffer_size())) {
|
if ($buffer = $this->fread($this->getid3->fread_buffer_size())) {
|
||||||
$MIDIdata .= $buffer;
|
$MIDIdata .= $buffer;
|
||||||
} else {
|
} else {
|
||||||
$info['warning'][] = 'only processed '.($i - 1).' of '.$thisfile_midi_raw['tracks'].' tracks';
|
$this->warning('only processed '.($i - 1).' of '.$thisfile_midi_raw['tracks'].' tracks');
|
||||||
$info['error'][] = 'Unabled to read more file data at '.$this->ftell().' (trying to seek to : '.$offset.'), was expecting at least 8 more bytes';
|
$this->error('Unabled to read more file data at '.$this->ftell().' (trying to seek to : '.$offset.'), was expecting at least 8 more bytes');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$trackID = substr($MIDIdata, $offset, 4);
|
$trackID = substr($MIDIdata, $offset, 4);
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
if ($trackID == GETID3_MIDI_MAGIC_MTRK) {
|
if ($trackID == GETID3_MIDI_MAGIC_MTRK) {
|
||||||
$tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
$tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
//$thisfile_midi['tracks'][$i]['size'] = $tracksize;
|
//$thisfile_midi['tracks'][$i]['size'] = $tracksize;
|
||||||
$trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
|
$trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
|
||||||
$offset += $tracksize;
|
$offset += $tracksize;
|
||||||
} else {
|
} else {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTRK).'" at '.($offset - 4).', found "'.getid3_lib::PrintHexBytes($trackID).'" instead';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTRK).'" at '.($offset - 4).', found "'.getid3_lib::PrintHexBytes($trackID).'" instead');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($trackdataarray) || !is_array($trackdataarray)) {
|
if (!isset($trackdataarray) || !is_array($trackdataarray)) {
|
||||||
$info['error'][] = 'Cannot find MIDI track information';
|
$this->error('Cannot find MIDI track information');
|
||||||
unset($thisfile_midi);
|
unset($thisfile_midi);
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important
|
if ($this->scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important
|
||||||
$thisfile_midi['totalticks'] = 0;
|
$thisfile_midi['totalticks'] = 0;
|
||||||
$info['playtime_seconds'] = 0;
|
$info['playtime_seconds'] = 0;
|
||||||
$CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
$CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
||||||
$CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
$CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat
|
||||||
$MicroSecondsPerQuarterNoteAfter = [];
|
$MicroSecondsPerQuarterNoteAfter = array ();
|
||||||
|
|
||||||
foreach ($trackdataarray as $tracknumber => $trackdata) {
|
foreach ($trackdataarray as $tracknumber => $trackdata) {
|
||||||
$eventsoffset = 0;
|
|
||||||
$LastIssuedMIDIcommand = 0;
|
|
||||||
$LastIssuedMIDIchannel = 0;
|
|
||||||
$CumulativeDeltaTime = 0;
|
|
||||||
$TicksAtCurrentBPM = 0;
|
|
||||||
while ($eventsoffset < strlen($trackdata)) {
|
|
||||||
$eventid = 0;
|
|
||||||
if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
|
|
||||||
$eventid = count($MIDIevents[$tracknumber]);
|
|
||||||
}
|
|
||||||
$deltatime = 0;
|
|
||||||
for ($i = 0; $i < 4; $i++) {
|
|
||||||
$deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
|
|
||||||
$deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
|
|
||||||
if ($deltatimebyte & 0x80) {
|
|
||||||
// another byte follows
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$CumulativeDeltaTime += $deltatime;
|
|
||||||
$TicksAtCurrentBPM += $deltatime;
|
|
||||||
$MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
|
|
||||||
$MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
|
|
||||||
if ($MIDI_event_channel & 0x80) {
|
|
||||||
// OK, normal event - MIDI command has MSB set
|
|
||||||
$LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
|
|
||||||
$LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
|
|
||||||
} else {
|
|
||||||
// running event - assume last command
|
|
||||||
$eventsoffset--;
|
|
||||||
}
|
|
||||||
$MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
|
|
||||||
$MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
|
|
||||||
if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released)
|
|
||||||
|
|
||||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
$eventsoffset = 0;
|
||||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
$LastIssuedMIDIcommand = 0;
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed)
|
$LastIssuedMIDIchannel = 0;
|
||||||
|
$CumulativeDeltaTime = 0;
|
||||||
|
$TicksAtCurrentBPM = 0;
|
||||||
|
while ($eventsoffset < strlen($trackdata)) {
|
||||||
|
$eventid = 0;
|
||||||
|
if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
|
||||||
|
$eventid = count($MIDIevents[$tracknumber]);
|
||||||
|
}
|
||||||
|
$deltatime = 0;
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
|
$deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
|
||||||
|
if ($deltatimebyte & 0x80) {
|
||||||
|
// another byte follows
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$CumulativeDeltaTime += $deltatime;
|
||||||
|
$TicksAtCurrentBPM += $deltatime;
|
||||||
|
$MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
|
||||||
|
$MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
|
if ($MIDI_event_channel & 0x80) {
|
||||||
|
// OK, normal event - MIDI command has MSB set
|
||||||
|
$LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
|
||||||
|
$LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
|
||||||
|
} else {
|
||||||
|
// running event - assume last command
|
||||||
|
$eventsoffset--;
|
||||||
|
}
|
||||||
|
$MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
|
||||||
|
$MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
|
||||||
|
if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released)
|
||||||
|
|
||||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch
|
|
||||||
|
|
||||||
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed)
|
||||||
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change
|
|
||||||
|
|
||||||
$controllernum = ord(substr($trackdata, $eventsoffset++, 1));
|
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$newvalue = ord(substr($trackdata, $eventsoffset++, 1));
|
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change
|
|
||||||
|
|
||||||
$newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch
|
||||||
|
|
||||||
$thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
|
$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
if ($tracknumber == 10) {
|
$velocity = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
|
|
||||||
} else {
|
|
||||||
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
|
|
||||||
}
|
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch
|
|
||||||
|
|
||||||
$channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change
|
||||||
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change)
|
|
||||||
|
|
||||||
$changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
|
$controllernum = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
|
$newvalue = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
|
|
||||||
} elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
|
|
||||||
$METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
|
|
||||||
$METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
|
|
||||||
$METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
|
|
||||||
$eventsoffset += $METAeventLength;
|
|
||||||
switch ($METAeventCommand) {
|
|
||||||
case 0x00: // Set track sequence number
|
|
||||||
$track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x01: // Text: generic
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change
|
||||||
$text_generic = substr($METAeventData, 0, $METAeventLength);
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
|
|
||||||
$thisfile_midi['comments']['comment'][] = $text_generic;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x02: // Text: copyright
|
$newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$text_copyright = substr($METAeventData, 0, $METAeventLength);
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
|
|
||||||
$thisfile_midi['comments']['copyright'][] = $text_copyright;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x03: // Text: track name
|
$thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
|
||||||
$text_trackname = substr($METAeventData, 0, $METAeventLength);
|
if ($tracknumber == 10) {
|
||||||
$thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
|
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
|
||||||
break;
|
} else {
|
||||||
|
$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
|
||||||
|
}
|
||||||
|
|
||||||
case 0x04: // Text: track instrument name
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch
|
||||||
$text_instrument = substr($METAeventData, 0, $METAeventLength);
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x05: // Text: lyrics
|
$channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$text_lyrics = substr($METAeventData, 0, $METAeventLength);
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
|
|
||||||
if (!isset($thisfile_midi['lyrics'])) {
|
|
||||||
$thisfile_midi['lyrics'] = '';
|
|
||||||
}
|
|
||||||
$thisfile_midi['lyrics'] .= $text_lyrics."\n";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x06: // Text: marker
|
} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change)
|
||||||
$text_marker = substr($METAeventData, 0, $METAeventLength);
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x07: // Text: cue point
|
$changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$text_cuepoint = substr($METAeventData, 0, $METAeventLength);
|
$changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
|
$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2F: // End Of Track
|
} elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x51: // Tempo: microseconds / quarter note
|
$METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
$CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
$METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
|
||||||
if ($CurrentMicroSecondsPerBeat == 0) {
|
$METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
|
||||||
$info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
|
$eventsoffset += $METAeventLength;
|
||||||
return false;
|
switch ($METAeventCommand) {
|
||||||
}
|
case 0x00: // Set track sequence number
|
||||||
$thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
|
$track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
||||||
$CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
|
||||||
$MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
|
break;
|
||||||
$TicksAtCurrentBPM = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x58: // Time signature
|
case 0x01: // Text: generic
|
||||||
$timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
|
$text_generic = substr($METAeventData, 0, $METAeventLength);
|
||||||
$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
|
||||||
$timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note
|
$thisfile_midi['comments']['comment'][] = $text_generic;
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote;
|
break;
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator;
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator;
|
|
||||||
$thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x59: // Keysignature
|
case 0x02: // Text: copyright
|
||||||
$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
|
$text_copyright = substr($METAeventData, 0, $METAeventLength);
|
||||||
if ($keysig_sharpsflats & 0x80) {
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
|
||||||
// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
|
$thisfile_midi['comments']['copyright'][] = $text_copyright;
|
||||||
$keysig_sharpsflats -= 256;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
$keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
|
case 0x03: // Text: track name
|
||||||
$keysigs = [-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#'];
|
$text_trackname = substr($METAeventData, 0, $METAeventLength);
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
|
$thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
|
break;
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor;
|
|
||||||
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
|
|
||||||
|
|
||||||
// $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
|
case 0x04: // Text: track instrument name
|
||||||
$thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
|
$text_instrument = substr($METAeventData, 0, $METAeventLength);
|
||||||
break;
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x7F: // Sequencer specific information
|
case 0x05: // Text: lyrics
|
||||||
$custom_data = substr($METAeventData, 0, $METAeventLength);
|
$text_lyrics = substr($METAeventData, 0, $METAeventLength);
|
||||||
break;
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
|
||||||
|
if (!isset($thisfile_midi['lyrics'])) {
|
||||||
|
$thisfile_midi['lyrics'] = '';
|
||||||
|
}
|
||||||
|
$thisfile_midi['lyrics'] .= $text_lyrics."\n";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case 0x06: // Text: marker
|
||||||
$info['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand;
|
$text_marker = substr($METAeventData, 0, $METAeventLength);
|
||||||
break;
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
|
|
||||||
$thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$previoustickoffset = null;
|
|
||||||
|
|
||||||
ksort($MicroSecondsPerQuarterNoteAfter);
|
case 0x07: // Text: cue point
|
||||||
foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
|
$text_cuepoint = substr($METAeventData, 0, $METAeventLength);
|
||||||
if (is_null($previoustickoffset)) {
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
|
||||||
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
break;
|
||||||
$previoustickoffset = $tickoffset;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($thisfile_midi['totalticks'] > $tickoffset) {
|
|
||||||
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
|
||||||
$info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
|
case 0x2F: // End Of Track
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
|
||||||
|
break;
|
||||||
|
|
||||||
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
case 0x51: // Tempo: microseconds / quarter note
|
||||||
$previoustickoffset = $tickoffset;
|
$CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
|
||||||
}
|
if ($CurrentMicroSecondsPerBeat == 0) {
|
||||||
}
|
$this->error('Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero');
|
||||||
if ($thisfile_midi['totalticks'] > $previoustickoffset) {
|
return false;
|
||||||
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
}
|
||||||
$info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
|
$thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
|
||||||
return false;
|
$CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
|
||||||
}
|
$MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
|
||||||
|
$TicksAtCurrentBPM = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
$info['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000);
|
case 0x58: // Time signature
|
||||||
}
|
$timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
|
||||||
}
|
$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
|
||||||
|
$timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote;
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator;
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator;
|
||||||
|
$thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x59: // Keysignature
|
||||||
|
$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
|
||||||
|
if ($keysig_sharpsflats & 0x80) {
|
||||||
|
// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
|
||||||
|
$keysig_sharpsflats -= 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
$keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
|
||||||
|
$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor;
|
||||||
|
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
|
||||||
|
|
||||||
|
// $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
|
||||||
|
$thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x7F: // Sequencer specific information
|
||||||
|
$custom_data = substr($METAeventData, 0, $METAeventLength);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->warning('Unhandled META Event Command: '.$METAeventCommand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->warning('Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
|
||||||
|
$thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$previoustickoffset = null;
|
||||||
|
|
||||||
|
ksort($MicroSecondsPerQuarterNoteAfter);
|
||||||
|
foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
|
||||||
|
if (is_null($previoustickoffset)) {
|
||||||
|
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
||||||
|
$previoustickoffset = $tickoffset;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($thisfile_midi['totalticks'] > $tickoffset) {
|
||||||
|
|
||||||
|
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
||||||
|
$this->error('Corrupt MIDI file: ticksperqnote == zero');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
|
||||||
|
|
||||||
|
$prevmicrosecondsperbeat = $microsecondsperbeat;
|
||||||
|
$previoustickoffset = $tickoffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($thisfile_midi['totalticks'] > $previoustickoffset) {
|
||||||
|
|
||||||
|
if ($thisfile_midi_raw['ticksperqnote'] == 0) {
|
||||||
|
$this->error('Corrupt MIDI file: ticksperqnote == zero');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!empty($info['playtime_seconds'])) {
|
if (!empty($info['playtime_seconds'])) {
|
||||||
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($thisfile_midi['lyrics'])) {
|
if (!empty($thisfile_midi['lyrics'])) {
|
||||||
$thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
|
$thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GeneralMIDIinstrumentLookup($instrumentid)
|
public function GeneralMIDIinstrumentLookup($instrumentid) {
|
||||||
{
|
|
||||||
|
|
||||||
$begin = __LINE__;
|
$begin = __LINE__;
|
||||||
|
|
||||||
/** This is not a comment!
|
/** This is not a comment!
|
||||||
|
|
||||||
0 Acoustic Grand
|
0 Acoustic Grand
|
||||||
1 Bright Acoustic
|
1 Bright Acoustic
|
||||||
2 Electric Grand
|
2 Electric Grand
|
||||||
3 Honky-Tonk
|
3 Honky-Tonk
|
||||||
4 Electric Piano 1
|
4 Electric Piano 1
|
||||||
5 Electric Piano 2
|
5 Electric Piano 2
|
||||||
6 Harpsichord
|
6 Harpsichord
|
||||||
7 Clavier
|
7 Clavier
|
||||||
8 Celesta
|
8 Celesta
|
||||||
9 Glockenspiel
|
9 Glockenspiel
|
||||||
10 Music Box
|
10 Music Box
|
||||||
11 Vibraphone
|
11 Vibraphone
|
||||||
12 Marimba
|
12 Marimba
|
||||||
13 Xylophone
|
13 Xylophone
|
||||||
14 Tubular Bells
|
14 Tubular Bells
|
||||||
15 Dulcimer
|
15 Dulcimer
|
||||||
16 Drawbar Organ
|
16 Drawbar Organ
|
||||||
17 Percussive Organ
|
17 Percussive Organ
|
||||||
18 Rock Organ
|
18 Rock Organ
|
||||||
19 Church Organ
|
19 Church Organ
|
||||||
20 Reed Organ
|
20 Reed Organ
|
||||||
21 Accordian
|
21 Accordian
|
||||||
22 Harmonica
|
22 Harmonica
|
||||||
23 Tango Accordian
|
23 Tango Accordian
|
||||||
24 Acoustic Guitar (nylon)
|
24 Acoustic Guitar (nylon)
|
||||||
25 Acoustic Guitar (steel)
|
25 Acoustic Guitar (steel)
|
||||||
26 Electric Guitar (jazz)
|
26 Electric Guitar (jazz)
|
||||||
27 Electric Guitar (clean)
|
27 Electric Guitar (clean)
|
||||||
28 Electric Guitar (muted)
|
28 Electric Guitar (muted)
|
||||||
29 Overdriven Guitar
|
29 Overdriven Guitar
|
||||||
30 Distortion Guitar
|
30 Distortion Guitar
|
||||||
31 Guitar Harmonics
|
31 Guitar Harmonics
|
||||||
32 Acoustic Bass
|
32 Acoustic Bass
|
||||||
33 Electric Bass (finger)
|
33 Electric Bass (finger)
|
||||||
34 Electric Bass (pick)
|
34 Electric Bass (pick)
|
||||||
35 Fretless Bass
|
35 Fretless Bass
|
||||||
36 Slap Bass 1
|
36 Slap Bass 1
|
||||||
37 Slap Bass 2
|
37 Slap Bass 2
|
||||||
38 Synth Bass 1
|
38 Synth Bass 1
|
||||||
39 Synth Bass 2
|
39 Synth Bass 2
|
||||||
40 Violin
|
40 Violin
|
||||||
41 Viola
|
41 Viola
|
||||||
42 Cello
|
42 Cello
|
||||||
43 Contrabass
|
43 Contrabass
|
||||||
44 Tremolo Strings
|
44 Tremolo Strings
|
||||||
45 Pizzicato Strings
|
45 Pizzicato Strings
|
||||||
46 Orchestral Strings
|
46 Orchestral Strings
|
||||||
47 Timpani
|
47 Timpani
|
||||||
48 String Ensemble 1
|
48 String Ensemble 1
|
||||||
49 String Ensemble 2
|
49 String Ensemble 2
|
||||||
50 SynthStrings 1
|
50 SynthStrings 1
|
||||||
51 SynthStrings 2
|
51 SynthStrings 2
|
||||||
52 Choir Aahs
|
52 Choir Aahs
|
||||||
53 Voice Oohs
|
53 Voice Oohs
|
||||||
54 Synth Voice
|
54 Synth Voice
|
||||||
55 Orchestra Hit
|
55 Orchestra Hit
|
||||||
56 Trumpet
|
56 Trumpet
|
||||||
57 Trombone
|
57 Trombone
|
||||||
58 Tuba
|
58 Tuba
|
||||||
59 Muted Trumpet
|
59 Muted Trumpet
|
||||||
60 French Horn
|
60 French Horn
|
||||||
61 Brass Section
|
61 Brass Section
|
||||||
62 SynthBrass 1
|
62 SynthBrass 1
|
||||||
63 SynthBrass 2
|
63 SynthBrass 2
|
||||||
64 Soprano Sax
|
64 Soprano Sax
|
||||||
65 Alto Sax
|
65 Alto Sax
|
||||||
66 Tenor Sax
|
66 Tenor Sax
|
||||||
67 Baritone Sax
|
67 Baritone Sax
|
||||||
68 Oboe
|
68 Oboe
|
||||||
69 English Horn
|
69 English Horn
|
||||||
70 Bassoon
|
70 Bassoon
|
||||||
71 Clarinet
|
71 Clarinet
|
||||||
72 Piccolo
|
72 Piccolo
|
||||||
73 Flute
|
73 Flute
|
||||||
74 Recorder
|
74 Recorder
|
||||||
75 Pan Flute
|
75 Pan Flute
|
||||||
76 Blown Bottle
|
76 Blown Bottle
|
||||||
77 Shakuhachi
|
77 Shakuhachi
|
||||||
78 Whistle
|
78 Whistle
|
||||||
79 Ocarina
|
79 Ocarina
|
||||||
80 Lead 1 (square)
|
80 Lead 1 (square)
|
||||||
81 Lead 2 (sawtooth)
|
81 Lead 2 (sawtooth)
|
||||||
82 Lead 3 (calliope)
|
82 Lead 3 (calliope)
|
||||||
83 Lead 4 (chiff)
|
83 Lead 4 (chiff)
|
||||||
84 Lead 5 (charang)
|
84 Lead 5 (charang)
|
||||||
85 Lead 6 (voice)
|
85 Lead 6 (voice)
|
||||||
86 Lead 7 (fifths)
|
86 Lead 7 (fifths)
|
||||||
87 Lead 8 (bass + lead)
|
87 Lead 8 (bass + lead)
|
||||||
88 Pad 1 (new age)
|
88 Pad 1 (new age)
|
||||||
89 Pad 2 (warm)
|
89 Pad 2 (warm)
|
||||||
90 Pad 3 (polysynth)
|
90 Pad 3 (polysynth)
|
||||||
91 Pad 4 (choir)
|
91 Pad 4 (choir)
|
||||||
92 Pad 5 (bowed)
|
92 Pad 5 (bowed)
|
||||||
93 Pad 6 (metallic)
|
93 Pad 6 (metallic)
|
||||||
94 Pad 7 (halo)
|
94 Pad 7 (halo)
|
||||||
95 Pad 8 (sweep)
|
95 Pad 8 (sweep)
|
||||||
96 FX 1 (rain)
|
96 FX 1 (rain)
|
||||||
97 FX 2 (soundtrack)
|
97 FX 2 (soundtrack)
|
||||||
98 FX 3 (crystal)
|
98 FX 3 (crystal)
|
||||||
99 FX 4 (atmosphere)
|
99 FX 4 (atmosphere)
|
||||||
100 FX 5 (brightness)
|
100 FX 5 (brightness)
|
||||||
101 FX 6 (goblins)
|
101 FX 6 (goblins)
|
||||||
102 FX 7 (echoes)
|
102 FX 7 (echoes)
|
||||||
103 FX 8 (sci-fi)
|
103 FX 8 (sci-fi)
|
||||||
104 Sitar
|
104 Sitar
|
||||||
105 Banjo
|
105 Banjo
|
||||||
106 Shamisen
|
106 Shamisen
|
||||||
107 Koto
|
107 Koto
|
||||||
108 Kalimba
|
108 Kalimba
|
||||||
109 Bagpipe
|
109 Bagpipe
|
||||||
110 Fiddle
|
110 Fiddle
|
||||||
111 Shanai
|
111 Shanai
|
||||||
112 Tinkle Bell
|
112 Tinkle Bell
|
||||||
113 Agogo
|
113 Agogo
|
||||||
114 Steel Drums
|
114 Steel Drums
|
||||||
115 Woodblock
|
115 Woodblock
|
||||||
116 Taiko Drum
|
116 Taiko Drum
|
||||||
117 Melodic Tom
|
117 Melodic Tom
|
||||||
118 Synth Drum
|
118 Synth Drum
|
||||||
119 Reverse Cymbal
|
119 Reverse Cymbal
|
||||||
120 Guitar Fret Noise
|
120 Guitar Fret Noise
|
||||||
121 Breath Noise
|
121 Breath Noise
|
||||||
122 Seashore
|
122 Seashore
|
||||||
123 Bird Tweet
|
123 Bird Tweet
|
||||||
124 Telephone Ring
|
124 Telephone Ring
|
||||||
125 Helicopter
|
125 Helicopter
|
||||||
126 Applause
|
126 Applause
|
||||||
127 Gunshot
|
127 Gunshot
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
|
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GeneralMIDIpercussionLookup($instrumentid)
|
public function GeneralMIDIpercussionLookup($instrumentid) {
|
||||||
{
|
|
||||||
|
|
||||||
$begin = __LINE__;
|
$begin = __LINE__;
|
||||||
|
|
||||||
/** This is not a comment!
|
/** This is not a comment!
|
||||||
|
|
||||||
35 Acoustic Bass Drum
|
35 Acoustic Bass Drum
|
||||||
36 Bass Drum 1
|
36 Bass Drum 1
|
||||||
37 Side Stick
|
37 Side Stick
|
||||||
38 Acoustic Snare
|
38 Acoustic Snare
|
||||||
39 Hand Clap
|
39 Hand Clap
|
||||||
40 Electric Snare
|
40 Electric Snare
|
||||||
41 Low Floor Tom
|
41 Low Floor Tom
|
||||||
42 Closed Hi-Hat
|
42 Closed Hi-Hat
|
||||||
43 High Floor Tom
|
43 High Floor Tom
|
||||||
44 Pedal Hi-Hat
|
44 Pedal Hi-Hat
|
||||||
45 Low Tom
|
45 Low Tom
|
||||||
46 Open Hi-Hat
|
46 Open Hi-Hat
|
||||||
47 Low-Mid Tom
|
47 Low-Mid Tom
|
||||||
48 Hi-Mid Tom
|
48 Hi-Mid Tom
|
||||||
49 Crash Cymbal 1
|
49 Crash Cymbal 1
|
||||||
50 High Tom
|
50 High Tom
|
||||||
51 Ride Cymbal 1
|
51 Ride Cymbal 1
|
||||||
52 Chinese Cymbal
|
52 Chinese Cymbal
|
||||||
53 Ride Bell
|
53 Ride Bell
|
||||||
54 Tambourine
|
54 Tambourine
|
||||||
55 Splash Cymbal
|
55 Splash Cymbal
|
||||||
56 Cowbell
|
56 Cowbell
|
||||||
57 Crash Cymbal 2
|
57 Crash Cymbal 2
|
||||||
59 Ride Cymbal 2
|
59 Ride Cymbal 2
|
||||||
60 Hi Bongo
|
60 Hi Bongo
|
||||||
61 Low Bongo
|
61 Low Bongo
|
||||||
62 Mute Hi Conga
|
62 Mute Hi Conga
|
||||||
63 Open Hi Conga
|
63 Open Hi Conga
|
||||||
64 Low Conga
|
64 Low Conga
|
||||||
65 High Timbale
|
65 High Timbale
|
||||||
66 Low Timbale
|
66 Low Timbale
|
||||||
67 High Agogo
|
67 High Agogo
|
||||||
68 Low Agogo
|
68 Low Agogo
|
||||||
69 Cabasa
|
69 Cabasa
|
||||||
70 Maracas
|
70 Maracas
|
||||||
71 Short Whistle
|
71 Short Whistle
|
||||||
72 Long Whistle
|
72 Long Whistle
|
||||||
73 Short Guiro
|
73 Short Guiro
|
||||||
74 Long Guiro
|
74 Long Guiro
|
||||||
75 Claves
|
75 Claves
|
||||||
76 Hi Wood Block
|
76 Hi Wood Block
|
||||||
77 Low Wood Block
|
77 Low Wood Block
|
||||||
78 Mute Cuica
|
78 Mute Cuica
|
||||||
79 Open Cuica
|
79 Open Cuica
|
||||||
80 Mute Triangle
|
80 Mute Triangle
|
||||||
81 Open Triangle
|
81 Open Triangle
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
|
||||||
|
}
|
||||||
|
|
||||||
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,86 +18,82 @@
|
||||||
class getid3_mod extends getid3_handler
|
class getid3_mod extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
$fileheader = $this->fread(1088);
|
||||||
$fileheader = $this->fread(1088);
|
if (preg_match('#^IMPM#', $fileheader)) {
|
||||||
if (preg_match('#^IMPM#', $fileheader)) {
|
return $this->getITheaderFilepointer();
|
||||||
return $this->getITheaderFilepointer();
|
} elseif (preg_match('#^Extended Module#', $fileheader)) {
|
||||||
} elseif (preg_match('#^Extended Module#', $fileheader)) {
|
return $this->getXMheaderFilepointer();
|
||||||
return $this->getXMheaderFilepointer();
|
} elseif (preg_match('#^.{44}SCRM#', $fileheader)) {
|
||||||
} elseif (preg_match('#^.{44}SCRM#', $fileheader)) {
|
return $this->getS3MheaderFilepointer();
|
||||||
return $this->getS3MheaderFilepointer();
|
} elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#', $fileheader)) {
|
||||||
} elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#', $fileheader)) {
|
return $this->getMODheaderFilepointer();
|
||||||
return $this->getMODheaderFilepointer();
|
}
|
||||||
}
|
$this->error('This is not a known type of MOD file');
|
||||||
$info['error'][] = 'This is not a known type of MOD file';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getMODheaderFilepointer()
|
public function getMODheaderFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset'] + 1080);
|
||||||
$this->fseek($info['avdataoffset'] + 1080);
|
$FormatID = $this->fread(4);
|
||||||
$FormatID = $this->fread(4);
|
if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) {
|
||||||
if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) {
|
$this->error('This is not a known type of MOD file');
|
||||||
$info['error'][] = 'This is not a known type of MOD file';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$info['fileformat'] = 'mod';
|
$info['fileformat'] = 'mod';
|
||||||
|
|
||||||
$info['error'][] = 'MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getXMheaderFilepointer()
|
public function getXMheaderFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
$FormatID = $this->fread(15);
|
||||||
$FormatID = $this->fread(15);
|
if (!preg_match('#^Extended Module$#', $FormatID)) {
|
||||||
if (!preg_match('#^Extended Module$#', $FormatID)) {
|
$this->error('This is not a known type of XM-MOD file');
|
||||||
$info['error'][] = 'This is not a known type of XM-MOD file';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$info['fileformat'] = 'xm';
|
$info['fileformat'] = 'xm';
|
||||||
|
|
||||||
$info['error'][] = 'XM-MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('XM-MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getS3MheaderFilepointer()
|
public function getS3MheaderFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset'] + 44);
|
||||||
$this->fseek($info['avdataoffset'] + 44);
|
$FormatID = $this->fread(4);
|
||||||
$FormatID = $this->fread(4);
|
if (!preg_match('#^SCRM$#', $FormatID)) {
|
||||||
if (!preg_match('#^SCRM$#', $FormatID)) {
|
$this->error('This is not a ScreamTracker MOD file');
|
||||||
$info['error'][] = 'This is not a ScreamTracker MOD file';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$info['fileformat'] = 's3m';
|
$info['fileformat'] = 's3m';
|
||||||
|
|
||||||
$info['error'][] = 'ScreamTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('ScreamTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getITheaderFilepointer()
|
public function getITheaderFilepointer() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
$FormatID = $this->fread(4);
|
||||||
$FormatID = $this->fread(4);
|
if (!preg_match('#^IMPM$#', $FormatID)) {
|
||||||
if (!preg_match('#^IMPM$#', $FormatID)) {
|
$this->error('This is not an ImpulseTracker MOD file');
|
||||||
$info['error'][] = 'This is not an ImpulseTracker MOD file';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$info['fileformat'] = 'it';
|
$info['fileformat'] = 'it';
|
||||||
|
|
||||||
|
$this->error('ImpulseTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$info['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,189 +18,187 @@
|
||||||
class getid3_monkey extends getid3_handler
|
class getid3_monkey extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
|
// 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
|
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||||
|
|
||||||
$info['fileformat'] = 'mac';
|
$info['fileformat'] = 'mac';
|
||||||
$info['audio']['dataformat'] = 'mac';
|
$info['audio']['dataformat'] = 'mac';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
|
|
||||||
$info['monkeys_audio']['raw'] = [];
|
$info['monkeys_audio']['raw'] = array();
|
||||||
$thisfile_monkeysaudio = &$info['monkeys_audio'];
|
$thisfile_monkeysaudio = &$info['monkeys_audio'];
|
||||||
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
|
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$MACheaderData = $this->fread(74);
|
$MACheaderData = $this->fread(74);
|
||||||
|
|
||||||
$thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
|
$thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
|
||||||
$magic = 'MAC ';
|
$magic = 'MAC ';
|
||||||
if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
|
if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_monkeysaudio_raw['magic']).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_monkeysaudio_raw['magic']).'"');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
|
$thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
|
||||||
|
|
||||||
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
||||||
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
|
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
|
||||||
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2));
|
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2));
|
||||||
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 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['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4));
|
||||||
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 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['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4));
|
||||||
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 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['nFinalFrameSamples'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4));
|
||||||
$thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4));
|
$thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4));
|
||||||
$thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2));
|
$thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2));
|
||||||
$offset = 8;
|
$offset = 8;
|
||||||
} else {
|
} else {
|
||||||
$offset = 8;
|
$offset = 8;
|
||||||
// APE_DESCRIPTOR
|
// APE_DESCRIPTOR
|
||||||
$thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
|
$thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
|
||||||
$offset += 16;
|
$offset += 16;
|
||||||
|
|
||||||
// APE_HEADER
|
// APE_HEADER
|
||||||
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
$thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
$thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
$thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
$thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
$thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
$thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
|
$thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
|
||||||
$thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
|
$thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
|
||||||
$thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004);
|
$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']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008);
|
||||||
$thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010);
|
$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['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020);
|
||||||
$thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
|
$thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
|
||||||
$thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
|
$thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
|
||||||
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
|
||||||
$thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
|
$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['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16));
|
||||||
$thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
|
$thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
|
||||||
$info['audio']['channels'] = $thisfile_monkeysaudio['channels'];
|
$info['audio']['channels'] = $thisfile_monkeysaudio['channels'];
|
||||||
$thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
|
$thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
|
||||||
if ($thisfile_monkeysaudio['sample_rate'] == 0) {
|
if ($thisfile_monkeysaudio['sample_rate'] == 0) {
|
||||||
$info['error'][] = 'Corrupt MAC file: frequency == zero';
|
$this->error('Corrupt MAC file: frequency == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
|
$info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
|
||||||
if ($thisfile_monkeysaudio['flags']['peak_level']) {
|
if ($thisfile_monkeysaudio['flags']['peak_level']) {
|
||||||
$thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
|
$thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
|
||||||
$thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
|
$thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
|
||||||
}
|
}
|
||||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||||
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
|
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
|
||||||
} else {
|
} else {
|
||||||
$thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
|
$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'];
|
$thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
|
||||||
if ($thisfile_monkeysaudio['playtime'] == 0) {
|
if ($thisfile_monkeysaudio['playtime'] == 0) {
|
||||||
$info['error'][] = 'Corrupt MAC file: playtime == zero';
|
$this->error('Corrupt MAC file: playtime == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
|
$info['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
|
||||||
$thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset'];
|
$thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset'];
|
||||||
$thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
|
$thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
|
||||||
if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
|
if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
|
||||||
$info['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
|
$this->error('Corrupt MAC file: uncompressed_size == zero');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
|
$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'];
|
$thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio'];
|
||||||
$info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
|
$info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
|
||||||
|
|
||||||
// add size of MAC header to avdataoffset
|
// add size of MAC header to avdataoffset
|
||||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||||
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
|
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
|
||||||
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
|
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
|
||||||
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
|
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
|
||||||
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
|
$info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
|
||||||
|
|
||||||
$info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
|
$info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
|
||||||
} else {
|
} else {
|
||||||
$info['avdataoffset'] += $offset;
|
$info['avdataoffset'] += $offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
|
||||||
if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
|
if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
|
||||||
//$info['warning'][] = 'cFileMD5 is null';
|
//$this->warning('cFileMD5 is null');
|
||||||
} else {
|
} else {
|
||||||
$info['md5_data_source'] = '';
|
$info['md5_data_source'] = '';
|
||||||
$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
|
$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
|
||||||
for ($i = 0; $i < strlen($md5); $i++) {
|
for ($i = 0; $i < strlen($md5); $i++) {
|
||||||
$info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
|
$info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
|
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
|
||||||
unset($info['md5_data_source']);
|
unset($info['md5_data_source']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
|
||||||
$info['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2);
|
$info['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2);
|
||||||
$info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression';
|
$info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression';
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function MonkeyCompressionLevelNameLookup($compressionlevel)
|
public function MonkeyCompressionLevelNameLookup($compressionlevel) {
|
||||||
{
|
static $MonkeyCompressionLevelNameLookup = array(
|
||||||
static $MonkeyCompressionLevelNameLookup = [
|
0 => 'unknown',
|
||||||
0 => 'unknown',
|
1000 => 'fast',
|
||||||
1000 => 'fast',
|
2000 => 'normal',
|
||||||
2000 => 'normal',
|
3000 => 'high',
|
||||||
3000 => 'high',
|
4000 => 'extra-high',
|
||||||
4000 => 'extra-high',
|
5000 => 'insane'
|
||||||
5000 => 'insane'
|
);
|
||||||
];
|
return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
|
||||||
return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
|
}
|
||||||
}
|
|
||||||
|
public function MonkeySamplesPerFrame($versionid, $compressionlevel) {
|
||||||
|
if ($versionid >= 3950) {
|
||||||
|
return 73728 * 4;
|
||||||
|
} elseif ($versionid >= 3900) {
|
||||||
|
return 73728;
|
||||||
|
} elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
|
||||||
|
return 73728;
|
||||||
|
} else {
|
||||||
|
return 9216;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function MonkeySamplesPerFrame($versionid, $compressionlevel)
|
|
||||||
{
|
|
||||||
if ($versionid >= 3950) {
|
|
||||||
return 73728 * 4;
|
|
||||||
} elseif ($versionid >= 3900) {
|
|
||||||
return 73728;
|
|
||||||
} elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
|
|
||||||
return 73728;
|
|
||||||
} else {
|
|
||||||
return 9216;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,497 +18,490 @@
|
||||||
class getid3_mpc extends getid3_handler
|
class getid3_mpc extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
$info['mpc']['header'] = array();
|
||||||
$info['mpc']['header'] = [];
|
$thisfile_mpc_header = &$info['mpc']['header'];
|
||||||
$thisfile_mpc_header = &$info['mpc']['header'];
|
|
||||||
|
$info['fileformat'] = 'mpc';
|
||||||
$info['fileformat'] = 'mpc';
|
$info['audio']['dataformat'] = 'mpc';
|
||||||
$info['audio']['dataformat'] = 'mpc';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
|
||||||
$info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
|
$info['audio']['lossless'] = false;
|
||||||
$info['audio']['lossless'] = false;
|
|
||||||
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
$MPCheaderData = $this->fread(4);
|
||||||
$MPCheaderData = $this->fread(4);
|
$info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
|
||||||
$info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
|
if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) {
|
||||||
if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) {
|
|
||||||
// this is SV8
|
// this is SV8
|
||||||
return $this->ParseMPCsv8();
|
return $this->ParseMPCsv8();
|
||||||
} elseif (preg_match('#^MP\+#', $info['mpc']['header']['preamble'])) {
|
|
||||||
// this is SV7
|
} elseif (preg_match('#^MP\+#', $info['mpc']['header']['preamble'])) {
|
||||||
return $this->ParseMPCsv7();
|
|
||||||
} elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) {
|
// this is SV7
|
||||||
// this is SV4 - SV6, handle seperately
|
return $this->ParseMPCsv7();
|
||||||
return $this->ParseMPCsv6();
|
|
||||||
} else {
|
} 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)) {
|
||||||
$info['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($MPCheaderData, 0, 4)).'"';
|
|
||||||
unset($info['fileformat']);
|
// this is SV4 - SV6, handle seperately
|
||||||
unset($info['mpc']);
|
return $this->ParseMPCsv6();
|
||||||
return false;
|
|
||||||
}
|
} else {
|
||||||
return false;
|
|
||||||
}
|
$this->error('Expecting "MP+" or "MPCK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($MPCheaderData, 0, 4)).'"');
|
||||||
|
unset($info['fileformat']);
|
||||||
|
unset($info['mpc']);
|
||||||
public function ParseMPCsv8()
|
return false;
|
||||||
{
|
|
||||||
// this is SV8
|
}
|
||||||
// http://trac.musepack.net/trac/wiki/SV8Specification
|
return false;
|
||||||
|
}
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$thisfile_mpc_header = &$info['mpc']['header'];
|
|
||||||
|
public function ParseMPCsv8() {
|
||||||
$keyNameSize = 2;
|
// this is SV8
|
||||||
$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
|
// http://trac.musepack.net/trac/wiki/SV8Specification
|
||||||
|
|
||||||
$offset = $this->ftell();
|
$info = &$this->getid3->info;
|
||||||
while ($offset < $info['avdataend']) {
|
$thisfile_mpc_header = &$info['mpc']['header'];
|
||||||
$thisPacket = [];
|
|
||||||
$thisPacket['offset'] = $offset;
|
$keyNameSize = 2;
|
||||||
$packet_offset = 0;
|
$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
|
||||||
|
|
||||||
// Size is a variable-size field, could be 1-4 bytes (possibly more?)
|
$offset = $this->ftell();
|
||||||
// read enough data in and figure out the exact size later
|
while ($offset < $info['avdataend']) {
|
||||||
$MPCheaderData = $this->fread($keyNameSize + $maxHandledPacketLength);
|
$thisPacket = array();
|
||||||
$packet_offset += $keyNameSize;
|
$thisPacket['offset'] = $offset;
|
||||||
$thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
|
$packet_offset = 0;
|
||||||
$thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
|
|
||||||
if ($thisPacket['key'] == $thisPacket['key_name']) {
|
// Size is a variable-size field, could be 1-4 bytes (possibly more?)
|
||||||
$info['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
|
// read enough data in and figure out the exact size later
|
||||||
return false;
|
$MPCheaderData = $this->fread($keyNameSize + $maxHandledPacketLength);
|
||||||
}
|
$packet_offset += $keyNameSize;
|
||||||
$packetLength = 0;
|
$thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
|
||||||
$thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
|
$thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
|
||||||
if ($thisPacket['packet_size'] === false) {
|
if ($thisPacket['key'] == $thisPacket['key_name']) {
|
||||||
$info['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize);
|
$this->error('Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$packet_offset += $packetLength;
|
$packetLength = 0;
|
||||||
$offset += $thisPacket['packet_size'];
|
$thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
|
||||||
|
if ($thisPacket['packet_size'] === false) {
|
||||||
switch ($thisPacket['key']) {
|
$this->error('Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize));
|
||||||
case 'SH': // Stream Header
|
return false;
|
||||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
}
|
||||||
if ($moreBytesToRead > 0) {
|
$packet_offset += $packetLength;
|
||||||
$MPCheaderData .= $this->fread($moreBytesToRead);
|
$offset += $thisPacket['packet_size'];
|
||||||
}
|
|
||||||
$thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
|
switch ($thisPacket['key']) {
|
||||||
$packet_offset += 4;
|
case 'SH': // Stream Header
|
||||||
$thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||||
$packet_offset += 1;
|
if ($moreBytesToRead > 0) {
|
||||||
|
$MPCheaderData .= $this->fread($moreBytesToRead);
|
||||||
$packetLength = 0;
|
}
|
||||||
$thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
$thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
|
||||||
$packet_offset += $packetLength;
|
$packet_offset += 4;
|
||||||
|
$thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
$packetLength = 0;
|
$packet_offset += 1;
|
||||||
$thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
|
||||||
$packet_offset += $packetLength;
|
$packetLength = 0;
|
||||||
|
$thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||||
$otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
$packet_offset += $packetLength;
|
||||||
$packet_offset += 2;
|
|
||||||
$thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
|
$packetLength = 0;
|
||||||
$thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
|
$thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||||
$thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
|
$packet_offset += $packetLength;
|
||||||
$thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
|
|
||||||
$thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
|
$otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||||
$thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
|
$packet_offset += 2;
|
||||||
|
$thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
|
||||||
$thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
|
$thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
|
||||||
$thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
|
$thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
|
||||||
$thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
|
$thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
|
||||||
$thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
|
$thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
|
||||||
|
$thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
|
||||||
$info['audio']['channels'] = $thisPacket['channels'];
|
|
||||||
$info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
|
$thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
|
||||||
$info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
|
$thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
|
||||||
break;
|
$thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
|
||||||
|
|
||||||
case 'RG': // Replay Gain
|
$info['audio']['channels'] = $thisPacket['channels'];
|
||||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
$info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
|
||||||
if ($moreBytesToRead > 0) {
|
$info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
|
||||||
$MPCheaderData .= $this->fread($moreBytesToRead);
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
}
|
break;
|
||||||
$thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
|
||||||
$packet_offset += 1;
|
case 'RG': // Replay Gain
|
||||||
$thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||||
$packet_offset += 2;
|
if ($moreBytesToRead > 0) {
|
||||||
$thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
$MPCheaderData .= $this->fread($moreBytesToRead);
|
||||||
$packet_offset += 2;
|
}
|
||||||
$thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
$thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
$packet_offset += 2;
|
$packet_offset += 1;
|
||||||
$thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
$thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||||
$packet_offset += 2;
|
$packet_offset += 2;
|
||||||
|
$thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||||
if ($thisPacket['replaygain_title_gain']) {
|
$packet_offset += 2;
|
||||||
$info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain'];
|
$thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||||
}
|
$packet_offset += 2;
|
||||||
if ($thisPacket['replaygain_title_peak']) {
|
$thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
|
||||||
$info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak'];
|
$packet_offset += 2;
|
||||||
}
|
|
||||||
if ($thisPacket['replaygain_album_gain']) {
|
if ($thisPacket['replaygain_title_gain']) { $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; }
|
||||||
$info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain'];
|
if ($thisPacket['replaygain_title_peak']) { $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; }
|
||||||
}
|
if ($thisPacket['replaygain_album_gain']) { $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; }
|
||||||
if ($thisPacket['replaygain_album_peak']) {
|
if ($thisPacket['replaygain_album_peak']) { $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; }
|
||||||
$info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak'];
|
break;
|
||||||
}
|
|
||||||
break;
|
case 'EI': // Encoder Info
|
||||||
|
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
||||||
case 'EI': // Encoder Info
|
if ($moreBytesToRead > 0) {
|
||||||
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
|
$MPCheaderData .= $this->fread($moreBytesToRead);
|
||||||
if ($moreBytesToRead > 0) {
|
}
|
||||||
$MPCheaderData .= $this->fread($moreBytesToRead);
|
$profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
}
|
$packet_offset += 1;
|
||||||
$profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
$quality_int = (($profile_pns & 0xF0) >> 4);
|
||||||
$packet_offset += 1;
|
$quality_dec = (($profile_pns & 0x0E) >> 3);
|
||||||
$quality_int = (($profile_pns & 0xF0) >> 4);
|
$thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
|
||||||
$quality_dec = (($profile_pns & 0x0E) >> 3);
|
$thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
|
||||||
$thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
|
$thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
$thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
|
$packet_offset += 1;
|
||||||
$thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
$thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
$packet_offset += 1;
|
$packet_offset += 1;
|
||||||
$thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
$thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
||||||
$packet_offset += 1;
|
$packet_offset += 1;
|
||||||
$thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
|
$thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
|
||||||
$packet_offset += 1;
|
|
||||||
$thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
|
$info['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
|
||||||
|
$thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
|
||||||
$info['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
|
//$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['encoder_version'] = $info['audio']['encoder'];
|
$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
|
||||||
//$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
|
break;
|
||||||
$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;
|
||||||
case 'SO': // Seek Table Offset
|
$thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
||||||
$packetLength = 0;
|
$packet_offset += $packetLength;
|
||||||
$thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
|
break;
|
||||||
$packet_offset += $packetLength;
|
|
||||||
break;
|
case 'ST': // Seek Table
|
||||||
|
case 'SE': // Stream End
|
||||||
case 'ST': // Seek Table
|
case 'AP': // Audio Data
|
||||||
case 'SE': // Stream End
|
// nothing useful here, just skip this packet
|
||||||
case 'AP': // Audio Data
|
$thisPacket = array();
|
||||||
// nothing useful here, just skip this packet
|
break;
|
||||||
$thisPacket = [];
|
|
||||||
break;
|
default:
|
||||||
|
$this->error('Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']);
|
||||||
default:
|
return false;
|
||||||
$info['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
|
break;
|
||||||
return false;
|
}
|
||||||
break;
|
if (!empty($thisPacket)) {
|
||||||
}
|
$info['mpc']['packets'][] = $thisPacket;
|
||||||
if (!empty($thisPacket)) {
|
}
|
||||||
$info['mpc']['packets'][] = $thisPacket;
|
$this->fseek($offset);
|
||||||
}
|
}
|
||||||
$this->fseek($offset);
|
$thisfile_mpc_header['size'] = $offset;
|
||||||
}
|
return true;
|
||||||
$thisfile_mpc_header['size'] = $offset;
|
}
|
||||||
return true;
|
|
||||||
}
|
public function ParseMPCsv7() {
|
||||||
|
// this is SV7
|
||||||
public function ParseMPCsv7()
|
// http://www.uni-jena.de/~pfk/mpp/sv8/header.html
|
||||||
{
|
|
||||||
// this is SV7
|
$info = &$this->getid3->info;
|
||||||
// http://www.uni-jena.de/~pfk/mpp/sv8/header.html
|
$thisfile_mpc_header = &$info['mpc']['header'];
|
||||||
|
$offset = 0;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$thisfile_mpc_header = &$info['mpc']['header'];
|
$thisfile_mpc_header['size'] = 28;
|
||||||
$offset = 0;
|
$MPCheaderData = $info['mpc']['header']['preamble'];
|
||||||
|
$MPCheaderData .= $this->fread($thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble']));
|
||||||
$thisfile_mpc_header['size'] = 28;
|
$offset = strlen('MP+');
|
||||||
$MPCheaderData = $info['mpc']['header']['preamble'];
|
|
||||||
$MPCheaderData .= $this->fread($thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble']));
|
$StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
|
||||||
$offset = strlen('MP+');
|
$offset += 1;
|
||||||
|
$thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
|
||||||
$StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
|
$thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
|
||||||
$offset += 1;
|
$thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
||||||
$thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
|
$offset += 4;
|
||||||
$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));
|
if ($thisfile_mpc_header['stream_version_major'] != 7) {
|
||||||
$offset += 4;
|
$this->error('Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')');
|
||||||
|
return false;
|
||||||
if ($thisfile_mpc_header['stream_version_major'] != 7) {
|
}
|
||||||
$info['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);
|
||||||
$FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
|
||||||
$offset += 4;
|
$thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
|
||||||
$thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
|
$thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
|
||||||
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
|
$thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
|
||||||
$thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
|
$thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
|
||||||
$thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
|
$thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
|
||||||
$thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
|
$thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
|
||||||
$thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
|
|
||||||
$thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
|
$thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
||||||
$thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
|
$offset += 2;
|
||||||
|
$thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
||||||
$thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
$offset += 2;
|
||||||
$offset += 2;
|
|
||||||
$thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
$thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
|
$thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
||||||
$thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
|
$offset += 2;
|
||||||
$offset += 2;
|
|
||||||
$thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
|
$FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
||||||
$offset += 2;
|
$offset += 4;
|
||||||
|
$thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
|
||||||
$FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
|
$thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
|
||||||
$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));
|
||||||
$thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
|
$offset += 1;
|
||||||
$offset += 3;
|
|
||||||
$thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
|
$thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
|
||||||
$offset += 1;
|
$thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
|
||||||
|
if ($thisfile_mpc_header['sample_rate'] == 0) {
|
||||||
$thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
|
$this->error('Corrupt MPC file: frequency == zero');
|
||||||
$thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
|
return false;
|
||||||
if ($thisfile_mpc_header['sample_rate'] == 0) {
|
}
|
||||||
$info['error'][] = 'Corrupt MPC file: frequency == zero';
|
$info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
||||||
return false;
|
$thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $info['audio']['channels'];
|
||||||
}
|
|
||||||
$info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
$info['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $info['audio']['channels']) / $info['audio']['sample_rate'];
|
||||||
$thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $info['audio']['channels'];
|
if ($info['playtime_seconds'] == 0) {
|
||||||
|
$this->error('Corrupt MPC file: playtime_seconds == zero');
|
||||||
$info['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $info['audio']['channels']) / $info['audio']['sample_rate'];
|
return false;
|
||||||
if ($info['playtime_seconds'] == 0) {
|
}
|
||||||
$info['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
|
|
||||||
return false;
|
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
||||||
}
|
$info['avdataoffset'] += $thisfile_mpc_header['size'];
|
||||||
|
|
||||||
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
$info['avdataoffset'] += $thisfile_mpc_header['size'];
|
|
||||||
|
$thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak'];
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$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_peak'] = $thisfile_mpc_header['raw']['title_peak'];
|
$thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
|
||||||
$thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
|
} else {
|
||||||
if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
|
$thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
|
||||||
$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_peak'] = $thisfile_mpc_header['raw']['album_peak'];
|
$thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
|
||||||
$thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
|
} else {
|
||||||
if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
|
$thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
|
||||||
$thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
|
}
|
||||||
} else {
|
$thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
|
||||||
$thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;
|
|
||||||
;
|
$info['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
|
||||||
}
|
$info['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
|
||||||
$thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
|
|
||||||
|
if ($thisfile_mpc_header['title_peak'] > 0) {
|
||||||
$info['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
|
$info['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
|
||||||
$info['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
|
} elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
|
||||||
|
$info['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
|
||||||
if ($thisfile_mpc_header['title_peak'] > 0) {
|
}
|
||||||
$info['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
|
if ($thisfile_mpc_header['album_peak'] > 0) {
|
||||||
} elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
|
$info['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
|
||||||
$info['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
|
}
|
||||||
}
|
|
||||||
if ($thisfile_mpc_header['album_peak'] > 0) {
|
//$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
|
||||||
$info['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
|
$info['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
|
||||||
}
|
$info['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
|
||||||
|
$thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
|
||||||
//$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
|
|
||||||
$info['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
|
return true;
|
||||||
$info['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
|
}
|
||||||
$thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
|
|
||||||
|
public function ParseMPCsv6() {
|
||||||
return true;
|
// this is SV4 - SV6
|
||||||
}
|
|
||||||
|
$info = &$this->getid3->info;
|
||||||
public function ParseMPCsv6()
|
$thisfile_mpc_header = &$info['mpc']['header'];
|
||||||
{
|
$offset = 0;
|
||||||
// this is SV4 - SV6
|
|
||||||
|
$thisfile_mpc_header['size'] = 8;
|
||||||
$info = &$this->getid3->info;
|
$this->fseek($info['avdataoffset']);
|
||||||
$thisfile_mpc_header = &$info['mpc']['header'];
|
$MPCheaderData = $this->fread($thisfile_mpc_header['size']);
|
||||||
$offset = 0;
|
|
||||||
|
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
||||||
$thisfile_mpc_header['size'] = 8;
|
$info['avdataoffset'] += $thisfile_mpc_header['size'];
|
||||||
$this->fseek($info['avdataoffset']);
|
|
||||||
$MPCheaderData = $this->fread($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));
|
||||||
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
|
$HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
|
||||||
$info['avdataoffset'] += $thisfile_mpc_header['size'];
|
|
||||||
|
|
||||||
// Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
|
// DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
|
||||||
$HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4));
|
// aaaa aaaa abcd dddd dddd deee eeff ffff
|
||||||
$HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
|
//
|
||||||
|
// a = bitrate = anything
|
||||||
|
// b = IS = anything
|
||||||
// DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
|
// c = MS = anything
|
||||||
// aaaa aaaa abcd dddd dddd deee eeff ffff
|
// d = streamversion = 0000000004 or 0000000005 or 0000000006
|
||||||
//
|
// e = maxband = anything
|
||||||
// a = bitrate = anything
|
// f = blocksize = 000001 for SV5+, anything(?) for SV4
|
||||||
// b = IS = anything
|
|
||||||
// c = MS = anything
|
$thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
|
||||||
// d = streamversion = 0000000004 or 0000000005 or 0000000006
|
$thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
|
||||||
// e = maxband = anything
|
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
|
||||||
// f = blocksize = 000001 for SV5+, anything(?) for SV4
|
$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['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
|
$thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
|
||||||
$thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
|
$thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
|
||||||
$thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
|
|
||||||
$thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
|
switch ($thisfile_mpc_header['stream_version_major']) {
|
||||||
$thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
|
case 4:
|
||||||
$thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
|
$thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
|
||||||
$thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
|
break;
|
||||||
|
|
||||||
switch ($thisfile_mpc_header['stream_version_major']) {
|
case 5:
|
||||||
case 4:
|
case 6:
|
||||||
$thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
|
$thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
default:
|
||||||
case 6:
|
$info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
|
||||||
$thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
|
unset($info['mpc']);
|
||||||
break;
|
return false;
|
||||||
|
break;
|
||||||
default:
|
}
|
||||||
$info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
|
|
||||||
unset($info['mpc']);
|
if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
|
||||||
return false;
|
$this->warning('Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size']);
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
$thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
|
||||||
if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
|
$info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
||||||
$info['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
|
$thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels'];
|
||||||
}
|
|
||||||
|
if ($thisfile_mpc_header['target_bitrate'] == 0) {
|
||||||
$thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
$info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
|
} else {
|
||||||
$thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels'];
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
|
}
|
||||||
if ($thisfile_mpc_header['target_bitrate'] == 0) {
|
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
|
||||||
} else {
|
$info['audio']['bitrate'] = $info['mpc']['bitrate'];
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
|
||||||
}
|
|
||||||
|
return true;
|
||||||
$info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
|
}
|
||||||
$info['audio']['bitrate'] = $info['mpc']['bitrate'];
|
|
||||||
$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
|
|
||||||
|
public function MPCprofileNameLookup($profileid) {
|
||||||
return true;
|
static $MPCprofileNameLookup = array(
|
||||||
}
|
0 => 'no profile',
|
||||||
|
1 => 'Experimental',
|
||||||
|
2 => 'unused',
|
||||||
public function MPCprofileNameLookup($profileid)
|
3 => 'unused',
|
||||||
{
|
4 => 'unused',
|
||||||
static $MPCprofileNameLookup = [
|
5 => 'below Telephone (q = 0.0)',
|
||||||
0 => 'no profile',
|
6 => 'below Telephone (q = 1.0)',
|
||||||
1 => 'Experimental',
|
7 => 'Telephone (q = 2.0)',
|
||||||
2 => 'unused',
|
8 => 'Thumb (q = 3.0)',
|
||||||
3 => 'unused',
|
9 => 'Radio (q = 4.0)',
|
||||||
4 => 'unused',
|
10 => 'Standard (q = 5.0)',
|
||||||
5 => 'below Telephone (q = 0.0)',
|
11 => 'Extreme (q = 6.0)',
|
||||||
6 => 'below Telephone (q = 1.0)',
|
12 => 'Insane (q = 7.0)',
|
||||||
7 => 'Telephone (q = 2.0)',
|
13 => 'BrainDead (q = 8.0)',
|
||||||
8 => 'Thumb (q = 3.0)',
|
14 => 'above BrainDead (q = 9.0)',
|
||||||
9 => 'Radio (q = 4.0)',
|
15 => 'above BrainDead (q = 10.0)'
|
||||||
10 => 'Standard (q = 5.0)',
|
);
|
||||||
11 => 'Extreme (q = 6.0)',
|
return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
|
||||||
12 => 'Insane (q = 7.0)',
|
}
|
||||||
13 => 'BrainDead (q = 8.0)',
|
|
||||||
14 => 'above BrainDead (q = 9.0)',
|
public function MPCfrequencyLookup($frequencyid) {
|
||||||
15 => 'above BrainDead (q = 10.0)'
|
static $MPCfrequencyLookup = array(
|
||||||
];
|
0 => 44100,
|
||||||
return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
|
1 => 48000,
|
||||||
}
|
2 => 37800,
|
||||||
|
3 => 32000
|
||||||
public function MPCfrequencyLookup($frequencyid)
|
);
|
||||||
{
|
return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
|
||||||
static $MPCfrequencyLookup = [
|
}
|
||||||
0 => 44100,
|
|
||||||
1 => 48000,
|
public function MPCpeakDBLookup($intvalue) {
|
||||||
2 => 37800,
|
if ($intvalue > 0) {
|
||||||
3 => 32000
|
return ((log10($intvalue) / log10(2)) - 15) * 6;
|
||||||
];
|
}
|
||||||
return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function MPCpeakDBLookup($intvalue)
|
public function MPCencoderVersionLookup($encoderversion) {
|
||||||
{
|
//Encoder version * 100 (106 = 1.06)
|
||||||
if ($intvalue > 0) {
|
//EncoderVersion % 10 == 0 Release (1.0)
|
||||||
return ((log10($intvalue) / log10(2)) - 15) * 6;
|
//EncoderVersion % 2 == 0 Beta (1.06)
|
||||||
}
|
//EncoderVersion % 2 == 1 Alpha (1.05a...z)
|
||||||
return false;
|
|
||||||
}
|
if ($encoderversion == 0) {
|
||||||
|
// very old version, not known exactly which
|
||||||
public function MPCencoderVersionLookup($encoderversion)
|
return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
|
||||||
{
|
}
|
||||||
//Encoder version * 100 (106 = 1.06)
|
|
||||||
//EncoderVersion % 10 == 0 Release (1.0)
|
if (($encoderversion % 10) == 0) {
|
||||||
//EncoderVersion % 2 == 0 Beta (1.06)
|
|
||||||
//EncoderVersion % 2 == 1 Alpha (1.05a...z)
|
// release version
|
||||||
|
return number_format($encoderversion / 100, 2);
|
||||||
if ($encoderversion == 0) {
|
|
||||||
// very old version, not known exactly which
|
} elseif (($encoderversion % 2) == 0) {
|
||||||
return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
|
|
||||||
}
|
// beta version
|
||||||
|
return number_format($encoderversion / 100, 2).' beta';
|
||||||
if (($encoderversion % 10) == 0) {
|
|
||||||
// release version
|
}
|
||||||
return number_format($encoderversion / 100, 2);
|
|
||||||
} elseif (($encoderversion % 2) == 0) {
|
// alpha version
|
||||||
// beta version
|
return number_format($encoderversion / 100, 2).' alpha';
|
||||||
return number_format($encoderversion / 100, 2).' beta';
|
}
|
||||||
}
|
|
||||||
|
public function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
|
||||||
// alpha version
|
$packet_size = 0;
|
||||||
return number_format($encoderversion / 100, 2).' alpha';
|
for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
|
||||||
}
|
// variable-length size field:
|
||||||
|
// bits, big-endian
|
||||||
public function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength = 9)
|
// 0xxx xxxx - value 0 to 2^7-1
|
||||||
{
|
// 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
|
||||||
$packet_size = 0;
|
// 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
|
||||||
for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
|
// 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
|
||||||
// variable-length size field:
|
// ...
|
||||||
// bits, big-endian
|
$thisbyte = ord(substr($data, ($packetLength - 1), 1));
|
||||||
// 0xxx xxxx - value 0 to 2^7-1
|
// look through bytes until find a byte with MSB==0
|
||||||
// 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
|
$packet_size = ($packet_size << 7);
|
||||||
// 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
|
$packet_size = ($packet_size | ($thisbyte & 0x7F));
|
||||||
// 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
|
if (($thisbyte & 0x80) === 0) {
|
||||||
// ...
|
break;
|
||||||
$thisbyte = ord(substr($data, ($packetLength - 1), 1));
|
}
|
||||||
// look through bytes until find a byte with MSB==0
|
if ($packetLength >= $maxHandledPacketLength) {
|
||||||
$packet_size = ($packet_size << 7);
|
return false;
|
||||||
$packet_size = ($packet_size | ($thisbyte & 0x7F));
|
}
|
||||||
if (($thisbyte & 0x80) === 0) {
|
}
|
||||||
break;
|
return $packet_size;
|
||||||
}
|
}
|
||||||
if ($packetLength >= $maxHandledPacketLength) {
|
|
||||||
return false;
|
public function MPCsv8PacketName($packetKey) {
|
||||||
}
|
static $MPCsv8PacketName = array();
|
||||||
}
|
if (empty($MPCsv8PacketName)) {
|
||||||
return $packet_size;
|
$MPCsv8PacketName = array(
|
||||||
}
|
'AP' => 'Audio Packet',
|
||||||
|
'CT' => 'Chapter Tag',
|
||||||
public function MPCsv8PacketName($packetKey)
|
'EI' => 'Encoder Info',
|
||||||
{
|
'RG' => 'Replay Gain',
|
||||||
static $MPCsv8PacketName = [];
|
'SE' => 'Stream End',
|
||||||
if (empty($MPCsv8PacketName)) {
|
'SH' => 'Stream Header',
|
||||||
$MPCsv8PacketName = [
|
'SO' => 'Seek Table Offset',
|
||||||
'AP' => 'Audio Packet',
|
'ST' => 'Seek Table',
|
||||||
'CT' => 'Chapter Tag',
|
);
|
||||||
'EI' => 'Encoder Info',
|
}
|
||||||
'RG' => 'Replay Gain',
|
return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
|
||||||
'SE' => 'Stream End',
|
}
|
||||||
'SH' => 'Stream Header',
|
|
||||||
'SO' => 'Seek Table Offset',
|
|
||||||
'ST' => 'Seek Table',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,405 +19,409 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
|
||||||
class getid3_optimfrog extends getid3_handler
|
class getid3_optimfrog extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
$info['fileformat'] = 'ofr';
|
||||||
$info['fileformat'] = 'ofr';
|
$info['audio']['dataformat'] = 'ofr';
|
||||||
$info['audio']['dataformat'] = 'ofr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['lossless'] = true;
|
|
||||||
|
$this->fseek($info['avdataoffset']);
|
||||||
$this->fseek($info['avdataoffset']);
|
$OFRheader = $this->fread(8);
|
||||||
$OFRheader = $this->fread(8);
|
if (substr($OFRheader, 0, 5) == '*RIFF') {
|
||||||
if (substr($OFRheader, 0, 5) == '*RIFF') {
|
|
||||||
return $this->ParseOptimFROGheader42();
|
return $this->ParseOptimFROGheader42();
|
||||||
} elseif (substr($OFRheader, 0, 3) == 'OFR') {
|
|
||||||
return $this->ParseOptimFROGheader45();
|
} elseif (substr($OFRheader, 0, 3) == 'OFR') {
|
||||||
}
|
|
||||||
|
return $this->ParseOptimFROGheader45();
|
||||||
$info['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($OFRheader).'"';
|
|
||||||
unset($info['fileformat']);
|
}
|
||||||
return false;
|
|
||||||
}
|
$this->error('Expecting "*RIFF" or "OFR " at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($OFRheader).'"');
|
||||||
|
unset($info['fileformat']);
|
||||||
|
return false;
|
||||||
public function ParseOptimFROGheader42()
|
}
|
||||||
{
|
|
||||||
// for fileformat of v4.21 and older
|
|
||||||
|
public function ParseOptimFROGheader42() {
|
||||||
$info = &$this->getid3->info;
|
// for fileformat of v4.21 and older
|
||||||
$this->fseek($info['avdataoffset']);
|
|
||||||
$OptimFROGheaderData = $this->fread(45);
|
$info = &$this->getid3->info;
|
||||||
$info['avdataoffset'] = 45;
|
$this->fseek($info['avdataoffset']);
|
||||||
|
$OptimFROGheaderData = $this->fread(45);
|
||||||
$OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
|
$info['avdataoffset'] = 45;
|
||||||
$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
|
|
||||||
$OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
|
$OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
|
||||||
$RIFFdata = substr($OptimFROGheaderData, 1, 44);
|
$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
|
||||||
$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
|
$OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
|
||||||
$OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
|
$RIFFdata = substr($OptimFROGheaderData, 1, 44);
|
||||||
|
$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
|
||||||
if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
|
$OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
|
||||||
$info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
|
||||||
$this->fseek($info['avdataend']);
|
if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
|
||||||
$RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
$info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
||||||
}
|
$this->fseek($info['avdataend']);
|
||||||
|
$RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
|
||||||
// move the data chunk after all other chunks (if any)
|
}
|
||||||
// so that the RIFF parser doesn't see EOF when trying
|
|
||||||
// to skip over the data chunk
|
// move the data chunk after all other chunks (if any)
|
||||||
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
|
// so that the RIFF parser doesn't see EOF when trying
|
||||||
|
// to skip over the data chunk
|
||||||
$getid3_temp = new getID3();
|
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
|
||||||
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
|
$getid3_temp = new getID3();
|
||||||
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
|
||||||
$getid3_riff->ParseRIFFdata($RIFFdata);
|
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
||||||
$info['riff'] = $getid3_temp->info['riff'];
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
|
$getid3_riff->ParseRIFFdata($RIFFdata);
|
||||||
$info['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
|
$info['riff'] = $getid3_temp->info['riff'];
|
||||||
$info['audio']['channels'] = $info['riff']['audio'][0]['channels'];
|
|
||||||
$info['audio']['sample_rate'] = $info['riff']['audio'][0]['sample_rate'];
|
$info['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
|
||||||
$info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
|
$info['audio']['channels'] = $info['riff']['audio'][0]['channels'];
|
||||||
$info['playtime_seconds'] = $OrignalRIFFdataSize / ($info['audio']['channels'] * $info['audio']['sample_rate'] * ($info['audio']['bits_per_sample'] / 8));
|
$info['audio']['sample_rate'] = $info['riff']['audio'][0]['sample_rate'];
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
|
||||||
|
$info['playtime_seconds'] = $OrignalRIFFdataSize / ($info['audio']['channels'] * $info['audio']['sample_rate'] * ($info['audio']['bits_per_sample'] / 8));
|
||||||
unset($getid3_riff, $getid3_temp, $RIFFdata);
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
return true;
|
unset($getid3_riff, $getid3_temp, $RIFFdata);
|
||||||
}
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
public function ParseOptimFROGheader45()
|
|
||||||
{
|
|
||||||
// for fileformat of v4.50a and higher
|
public function ParseOptimFROGheader45() {
|
||||||
|
// for fileformat of v4.50a and higher
|
||||||
$info = &$this->getid3->info;
|
|
||||||
$RIFFdata = '';
|
$info = &$this->getid3->info;
|
||||||
$this->fseek($info['avdataoffset']);
|
$RIFFdata = '';
|
||||||
while (!feof($this->getid3->fp) && ($this->ftell() < $info['avdataend'])) {
|
$this->fseek($info['avdataoffset']);
|
||||||
$BlockOffset = $this->ftell();
|
while (!feof($this->getid3->fp) && ($this->ftell() < $info['avdataend'])) {
|
||||||
$BlockData = $this->fread(8);
|
$BlockOffset = $this->ftell();
|
||||||
$offset = 8;
|
$BlockData = $this->fread(8);
|
||||||
$BlockName = substr($BlockData, 0, 4);
|
$offset = 8;
|
||||||
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
$BlockName = substr($BlockData, 0, 4);
|
||||||
|
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
||||||
if ($BlockName == 'OFRX') {
|
|
||||||
$BlockName = 'OFR ';
|
if ($BlockName == 'OFRX') {
|
||||||
}
|
$BlockName = 'OFR ';
|
||||||
if (!isset($info['ofr'][$BlockName])) {
|
}
|
||||||
$info['ofr'][$BlockName] = [];
|
if (!isset($info['ofr'][$BlockName])) {
|
||||||
}
|
$info['ofr'][$BlockName] = array();
|
||||||
$thisfile_ofr_thisblock = &$info['ofr'][$BlockName];
|
}
|
||||||
|
$thisfile_ofr_thisblock = &$info['ofr'][$BlockName];
|
||||||
switch ($BlockName) {
|
|
||||||
case 'OFR ':
|
switch ($BlockName) {
|
||||||
// shortcut
|
case 'OFR ':
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
// shortcut
|
||||||
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
$info['audio']['encoder'] = 'OptimFROG 4.50 alpha';
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
switch ($BlockSize) {
|
|
||||||
case 12:
|
$info['audio']['encoder'] = 'OptimFROG 4.50 alpha';
|
||||||
case 15:
|
switch ($BlockSize) {
|
||||||
// good
|
case 12:
|
||||||
break;
|
case 15:
|
||||||
|
// good
|
||||||
default:
|
break;
|
||||||
$info['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
|
|
||||||
break;
|
default:
|
||||||
}
|
$this->warning('"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)');
|
||||||
$BlockData .= $this->fread($BlockSize);
|
break;
|
||||||
|
}
|
||||||
$thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
|
$BlockData .= $this->fread($BlockSize);
|
||||||
$offset += 6;
|
|
||||||
$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
$thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
|
||||||
$thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
$offset += 6;
|
||||||
$offset += 1;
|
$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
$thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
||||||
$thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
|
$offset += 1;
|
||||||
$offset += 1;
|
$thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
$thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
|
||||||
$offset += 4;
|
$offset += 1;
|
||||||
|
$thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||||
if ($BlockSize > 12) {
|
$offset += 4;
|
||||||
// OFR 4.504b or higher
|
|
||||||
$thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
|
if ($BlockSize > 12) {
|
||||||
$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']);
|
// OFR 4.504b or higher
|
||||||
$offset += 2;
|
$thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
|
||||||
$thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
$thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||||
$thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
|
$thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
|
||||||
$thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
|
$offset += 2;
|
||||||
$offset += 1;
|
$thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||||
|
$thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
|
||||||
$info['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
|
$thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
|
||||||
$info['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
|
$offset += 1;
|
||||||
|
|
||||||
if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
|
$info['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
|
||||||
if (strtolower(getid3_lib::fileextension($info['filename'])) == 'ofs') {
|
$info['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
|
||||||
// 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.
|
if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
|
||||||
$info['audio']['dataformat'] = 'ofs';
|
if (strtolower(getid3_lib::fileextension($info['filename'])) == 'ofs') {
|
||||||
$info['audio']['lossless'] = true;
|
// 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.
|
||||||
}
|
$info['audio']['dataformat'] = 'ofs';
|
||||||
}
|
$info['audio']['lossless'] = true;
|
||||||
|
}
|
||||||
$info['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
|
}
|
||||||
$info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
|
|
||||||
$info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
}
|
||||||
break;
|
|
||||||
|
$info['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
|
||||||
|
$info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
|
||||||
case 'COMP':
|
$info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
|
||||||
// unlike other block types, there CAN be multiple COMP blocks
|
break;
|
||||||
|
|
||||||
$COMPdata['offset'] = $BlockOffset;
|
|
||||||
$COMPdata['size'] = $BlockSize;
|
case 'COMP':
|
||||||
|
// unlike other block types, there CAN be multiple COMP blocks
|
||||||
if ($info['avdataoffset'] == 0) {
|
|
||||||
$info['avdataoffset'] = $BlockOffset;
|
$COMPdata['offset'] = $BlockOffset;
|
||||||
}
|
$COMPdata['size'] = $BlockSize;
|
||||||
|
|
||||||
// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
|
if ($info['avdataoffset'] == 0) {
|
||||||
$BlockData .= $this->fread(14);
|
$info['avdataoffset'] = $BlockOffset;
|
||||||
$this->fseek($BlockSize - 14, SEEK_CUR);
|
}
|
||||||
|
|
||||||
$COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
|
||||||
$offset += 4;
|
$BlockData .= $this->fread(14);
|
||||||
$COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
$this->fseek($BlockSize - 14, SEEK_CUR);
|
||||||
$offset += 4;
|
|
||||||
$COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
$COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||||
$COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
|
$offset += 4;
|
||||||
$offset += 1;
|
$COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
|
||||||
$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
$offset += 4;
|
||||||
$COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
|
$COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$offset += 1;
|
$COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
|
||||||
$COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
$offset += 1;
|
||||||
//$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
|
$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
|
||||||
$offset += 2;
|
$COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
|
||||||
|
$offset += 1;
|
||||||
if ($info['ofr']['OFR ']['size'] > 12) {
|
$COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||||
// OFR 4.504b or higher
|
//$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
|
||||||
$COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
$offset += 2;
|
||||||
$COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
|
|
||||||
$offset += 2;
|
if ($info['ofr']['OFR ']['size'] > 12) {
|
||||||
}
|
|
||||||
|
// OFR 4.504b or higher
|
||||||
if ($COMPdata['crc_32'] == 0x454E4F4E) {
|
$COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
|
||||||
// ASCII value of 'NONE' - placeholder value in v4.50a
|
$COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
|
||||||
$COMPdata['crc_32'] = false;
|
$offset += 2;
|
||||||
}
|
|
||||||
|
}
|
||||||
$thisfile_ofr_thisblock[] = $COMPdata;
|
|
||||||
break;
|
if ($COMPdata['crc_32'] == 0x454E4F4E) {
|
||||||
|
// ASCII value of 'NONE' - placeholder value in v4.50a
|
||||||
case 'HEAD':
|
$COMPdata['crc_32'] = false;
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
}
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
|
||||||
|
$thisfile_ofr_thisblock[] = $COMPdata;
|
||||||
$RIFFdata .= $this->fread($BlockSize);
|
break;
|
||||||
break;
|
|
||||||
|
case 'HEAD':
|
||||||
case 'TAIL':
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
|
||||||
|
$RIFFdata .= $this->fread($BlockSize);
|
||||||
if ($BlockSize > 0) {
|
break;
|
||||||
$RIFFdata .= $this->fread($BlockSize);
|
|
||||||
}
|
case 'TAIL':
|
||||||
break;
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
case 'RECV':
|
|
||||||
// block contains no useful meta data - simply note and skip
|
if ($BlockSize > 0) {
|
||||||
|
$RIFFdata .= $this->fread($BlockSize);
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
}
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
break;
|
||||||
|
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
case 'RECV':
|
||||||
break;
|
// block contains no useful meta data - simply note and skip
|
||||||
|
|
||||||
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
case 'APET':
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
// APEtag v2
|
|
||||||
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
break;
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
|
||||||
$info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.$this->getid3->version().') of getID3()';
|
|
||||||
|
case 'APET':
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
// APEtag v2
|
||||||
break;
|
|
||||||
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
case 'MD5 ':
|
$this->warning('APEtag processing inside OptimFROG not supported in this version ('.$this->getid3->version().') of getID3()');
|
||||||
// APEtag v2
|
|
||||||
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
break;
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
|
||||||
|
|
||||||
if ($BlockSize == 16) {
|
case 'MD5 ':
|
||||||
$thisfile_ofr_thisblock['md5_binary'] = $this->fread($BlockSize);
|
// APEtag v2
|
||||||
$thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
|
|
||||||
$info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
} else {
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
$info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
|
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
if ($BlockSize == 16) {
|
||||||
}
|
|
||||||
break;
|
$thisfile_ofr_thisblock['md5_binary'] = $this->fread($BlockSize);
|
||||||
|
$thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
|
||||||
|
$info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
|
||||||
default:
|
|
||||||
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
} else {
|
||||||
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
|
||||||
|
$this->warning('Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead');
|
||||||
$info['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
if (isset($info['ofr']['TAIL']['offset'])) {
|
|
||||||
$info['avdataend'] = $info['ofr']['TAIL']['offset'];
|
default:
|
||||||
}
|
$thisfile_ofr_thisblock['offset'] = $BlockOffset;
|
||||||
|
$thisfile_ofr_thisblock['size'] = $BlockSize;
|
||||||
$info['playtime_seconds'] = (float) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']);
|
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$this->warning('Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']);
|
||||||
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
// move the data chunk after all other chunks (if any)
|
break;
|
||||||
// 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);
|
if (isset($info['ofr']['TAIL']['offset'])) {
|
||||||
|
$info['avdataend'] = $info['ofr']['TAIL']['offset'];
|
||||||
$getid3_temp = new getID3();
|
}
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
|
||||||
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
|
$info['playtime_seconds'] = (float) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']);
|
||||||
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
|
||||||
$getid3_riff->ParseRIFFdata($RIFFdata);
|
// move the data chunk after all other chunks (if any)
|
||||||
$info['riff'] = $getid3_temp->info['riff'];
|
// so that the RIFF parser doesn't see EOF when trying
|
||||||
|
// to skip over the data chunk
|
||||||
unset($getid3_riff, $getid3_temp, $RIFFdata);
|
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
|
||||||
|
|
||||||
return true;
|
$getid3_temp = new getID3();
|
||||||
}
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
|
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
|
||||||
|
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
||||||
public static function OptimFROGsampleTypeLookup($SampleType)
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
{
|
$getid3_riff->ParseRIFFdata($RIFFdata);
|
||||||
static $OptimFROGsampleTypeLookup = [
|
$info['riff'] = $getid3_temp->info['riff'];
|
||||||
0 => 'unsigned int (8-bit)',
|
|
||||||
1 => 'signed int (8-bit)',
|
unset($getid3_riff, $getid3_temp, $RIFFdata);
|
||||||
2 => 'unsigned int (16-bit)',
|
|
||||||
3 => 'signed int (16-bit)',
|
return true;
|
||||||
4 => 'unsigned int (24-bit)',
|
}
|
||||||
5 => 'signed int (24-bit)',
|
|
||||||
6 => 'unsigned int (32-bit)',
|
|
||||||
7 => 'signed int (32-bit)',
|
public static function OptimFROGsampleTypeLookup($SampleType) {
|
||||||
8 => 'float 0.24 (32-bit)',
|
static $OptimFROGsampleTypeLookup = array(
|
||||||
9 => 'float 16.8 (32-bit)',
|
0 => 'unsigned int (8-bit)',
|
||||||
10 => 'float 24.0 (32-bit)'
|
1 => 'signed int (8-bit)',
|
||||||
];
|
2 => 'unsigned int (16-bit)',
|
||||||
return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
|
3 => 'signed int (16-bit)',
|
||||||
}
|
4 => 'unsigned int (24-bit)',
|
||||||
|
5 => 'signed int (24-bit)',
|
||||||
public static function OptimFROGbitsPerSampleTypeLookup($SampleType)
|
6 => 'unsigned int (32-bit)',
|
||||||
{
|
7 => 'signed int (32-bit)',
|
||||||
static $OptimFROGbitsPerSampleTypeLookup = [
|
8 => 'float 0.24 (32-bit)',
|
||||||
0 => 8,
|
9 => 'float 16.8 (32-bit)',
|
||||||
1 => 8,
|
10 => 'float 24.0 (32-bit)'
|
||||||
2 => 16,
|
);
|
||||||
3 => 16,
|
return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
|
||||||
4 => 24,
|
}
|
||||||
5 => 24,
|
|
||||||
6 => 32,
|
public static function OptimFROGbitsPerSampleTypeLookup($SampleType) {
|
||||||
7 => 32,
|
static $OptimFROGbitsPerSampleTypeLookup = array(
|
||||||
8 => 32,
|
0 => 8,
|
||||||
9 => 32,
|
1 => 8,
|
||||||
10 => 32
|
2 => 16,
|
||||||
];
|
3 => 16,
|
||||||
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
|
4 => 24,
|
||||||
}
|
5 => 24,
|
||||||
|
6 => 32,
|
||||||
public static function OptimFROGchannelConfigurationLookup($ChannelConfiguration)
|
7 => 32,
|
||||||
{
|
8 => 32,
|
||||||
static $OptimFROGchannelConfigurationLookup = [
|
9 => 32,
|
||||||
0 => 'mono',
|
10 => 32
|
||||||
1 => 'stereo'
|
);
|
||||||
];
|
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
|
||||||
return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
|
}
|
||||||
}
|
|
||||||
|
public static function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
|
||||||
public static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration)
|
static $OptimFROGchannelConfigurationLookup = array(
|
||||||
{
|
0 => 'mono',
|
||||||
static $OptimFROGchannelConfigNumChannelsLookup = [
|
1 => 'stereo'
|
||||||
0 => 1,
|
);
|
||||||
1 => 2
|
return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
|
||||||
];
|
}
|
||||||
return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
|
|
||||||
}
|
public static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
|
||||||
|
static $OptimFROGchannelConfigNumChannelsLookup = array(
|
||||||
|
0 => 1,
|
||||||
|
1 => 2
|
||||||
// static function OptimFROGalgorithmNameLookup($AlgorithID) {
|
);
|
||||||
// static $OptimFROGalgorithmNameLookup = array();
|
return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
|
||||||
// return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
public static function OptimFROGencoderNameLookup($EncoderID)
|
// static function OptimFROGalgorithmNameLookup($AlgorithID) {
|
||||||
{
|
// static $OptimFROGalgorithmNameLookup = array();
|
||||||
// version = (encoderID >> 4) + 4500
|
// return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
|
||||||
// system = encoderID & 0xF
|
// }
|
||||||
|
|
||||||
$EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
|
|
||||||
$EncoderSystemID = ($EncoderID & 0x0F);
|
public static function OptimFROGencoderNameLookup($EncoderID) {
|
||||||
|
// version = (encoderID >> 4) + 4500
|
||||||
static $OptimFROGencoderSystemLookup = [
|
// system = encoderID & 0xF
|
||||||
0x00 => 'Windows console',
|
|
||||||
0x01 => 'Linux console',
|
$EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
|
||||||
0x0F => 'unknown'
|
$EncoderSystemID = ($EncoderID & 0x0F);
|
||||||
];
|
|
||||||
return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
|
static $OptimFROGencoderSystemLookup = array(
|
||||||
}
|
0x00 => 'Windows console',
|
||||||
|
0x01 => 'Linux console',
|
||||||
public static function OptimFROGcompressionLookup($CompressionID)
|
0x0F => 'unknown'
|
||||||
{
|
);
|
||||||
// mode = compression >> 3
|
return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
|
||||||
// speedup = compression & 0x07
|
}
|
||||||
|
|
||||||
$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
public static function OptimFROGcompressionLookup($CompressionID) {
|
||||||
//$CompressionSpeedupID = ($CompressionID & 0x07);
|
// mode = compression >> 3
|
||||||
|
// speedup = compression & 0x07
|
||||||
static $OptimFROGencoderModeLookup = [
|
|
||||||
0x00 => 'fast',
|
$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
||||||
0x01 => 'normal',
|
//$CompressionSpeedupID = ($CompressionID & 0x07);
|
||||||
0x02 => 'high',
|
|
||||||
0x03 => 'extra', // extranew (some versions)
|
static $OptimFROGencoderModeLookup = array(
|
||||||
0x04 => 'best', // bestnew (some versions)
|
0x00 => 'fast',
|
||||||
0x05 => 'ultra',
|
0x01 => 'normal',
|
||||||
0x06 => 'insane',
|
0x02 => 'high',
|
||||||
0x07 => 'highnew',
|
0x03 => 'extra', // extranew (some versions)
|
||||||
0x08 => 'extranew',
|
0x04 => 'best', // bestnew (some versions)
|
||||||
0x09 => 'bestnew'
|
0x05 => 'ultra',
|
||||||
];
|
0x06 => 'insane',
|
||||||
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
|
0x07 => 'highnew',
|
||||||
}
|
0x08 => 'extranew',
|
||||||
|
0x09 => 'bestnew'
|
||||||
public static function OptimFROGspeedupLookup($CompressionID)
|
);
|
||||||
{
|
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
|
||||||
// mode = compression >> 3
|
}
|
||||||
// speedup = compression & 0x07
|
|
||||||
|
public static function OptimFROGspeedupLookup($CompressionID) {
|
||||||
//$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
// mode = compression >> 3
|
||||||
$CompressionSpeedupID = ($CompressionID & 0x07);
|
// speedup = compression & 0x07
|
||||||
|
|
||||||
static $OptimFROGencoderSpeedupLookup = [
|
//$CompressionModeID = ($CompressionID & 0xF8) >> 3;
|
||||||
0x00 => '1x',
|
$CompressionSpeedupID = ($CompressionID & 0x07);
|
||||||
0x01 => '2x',
|
|
||||||
0x02 => '4x'
|
static $OptimFROGencoderSpeedupLookup = array(
|
||||||
];
|
0x00 => '1x',
|
||||||
return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
|
0x01 => '2x',
|
||||||
}
|
0x02 => '4x'
|
||||||
|
);
|
||||||
|
return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,76 +18,76 @@
|
||||||
class getid3_rkau extends getid3_handler
|
class getid3_rkau extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$RKAUHeader = $this->fread(20);
|
$RKAUHeader = $this->fread(20);
|
||||||
$magic = 'RKA';
|
$magic = 'RKA';
|
||||||
if (substr($RKAUHeader, 0, 3) != $magic) {
|
if (substr($RKAUHeader, 0, 3) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['fileformat'] = 'rkau';
|
$info['fileformat'] = 'rkau';
|
||||||
$info['audio']['dataformat'] = 'rkau';
|
$info['audio']['dataformat'] = 'rkau';
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
|
||||||
$info['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
|
$info['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
|
||||||
$info['rkau']['version'] = '1.'.str_pad($info['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
|
$info['rkau']['version'] = '1.'.str_pad($info['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
|
||||||
if (($info['rkau']['version'] > 1.07) || ($info['rkau']['version'] < 1.06)) {
|
if (($info['rkau']['version'] > 1.07) || ($info['rkau']['version'] < 1.06)) {
|
||||||
$info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] can only parse RKAU files v1.06 and 1.07 (this file is v'.$info['rkau']['version'].')';
|
$this->error('This version of getID3() ['.$this->getid3->version().'] can only parse RKAU files v1.06 and 1.07 (this file is v'.$info['rkau']['version'].')');
|
||||||
unset($info['rkau']);
|
unset($info['rkau']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
|
$info['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4));
|
||||||
$info['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
|
$info['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4));
|
||||||
$info['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
|
$info['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
|
||||||
$info['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
|
$info['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
|
||||||
|
|
||||||
$info['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
|
$info['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
|
||||||
$this->RKAUqualityLookup($info['rkau']);
|
$this->RKAUqualityLookup($info['rkau']);
|
||||||
|
|
||||||
$info['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
|
$info['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
|
||||||
$info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x01));
|
$info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x01));
|
||||||
$info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02);
|
$info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02);
|
||||||
$info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04);
|
$info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04);
|
||||||
|
|
||||||
if ($info['rkau']['flags']['streaming']) {
|
if ($info['rkau']['flags']['streaming']) {
|
||||||
$info['avdataoffset'] += 20;
|
$info['avdataoffset'] += 20;
|
||||||
$info['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
|
$info['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
|
||||||
} else {
|
} else {
|
||||||
$info['avdataoffset'] += 16;
|
$info['avdataoffset'] += 16;
|
||||||
$info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1;
|
$info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1;
|
||||||
}
|
}
|
||||||
// Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
|
// 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(?)
|
// sometimes it's more, sometimes less. No idea why(?)
|
||||||
|
|
||||||
$info['audio']['lossless'] = $info['rkau']['lossless'];
|
$info['audio']['lossless'] = $info['rkau']['lossless'];
|
||||||
$info['audio']['channels'] = $info['rkau']['channels'];
|
$info['audio']['channels'] = $info['rkau']['channels'];
|
||||||
$info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample'];
|
||||||
$info['audio']['sample_rate'] = $info['rkau']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['rkau']['sample_rate'];
|
||||||
|
|
||||||
$info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8));
|
$info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8));
|
||||||
$info['audio']['bitrate'] = ($info['rkau']['compressed_bytes'] * 8) / $info['playtime_seconds'];
|
$info['audio']['bitrate'] = ($info['rkau']['compressed_bytes'] * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function RKAUqualityLookup(&$RKAUdata)
|
public function RKAUqualityLookup(&$RKAUdata) {
|
||||||
{
|
$level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
|
||||||
$level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
|
$quality = $RKAUdata['raw']['quality'] & 0x0F;
|
||||||
$quality = $RKAUdata['raw']['quality'] & 0x0F;
|
|
||||||
|
|
||||||
$RKAUdata['lossless'] = (($quality == 0) ? true : false);
|
$RKAUdata['lossless'] = (($quality == 0) ? true : false);
|
||||||
$RKAUdata['compression_level'] = $level + 1;
|
$RKAUdata['compression_level'] = $level + 1;
|
||||||
if (!$RKAUdata['lossless']) {
|
if (!$RKAUdata['lossless']) {
|
||||||
$RKAUdata['quality_setting'] = $quality;
|
$RKAUdata['quality_setting'] = $quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,148 +18,165 @@
|
||||||
class getid3_shorten extends getid3_handler
|
class getid3_shorten extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
$ShortenHeader = $this->fread(8);
|
$ShortenHeader = $this->fread(8);
|
||||||
$magic = 'ajkg';
|
$magic = 'ajkg';
|
||||||
if (substr($ShortenHeader, 0, 4) != $magic) {
|
if (substr($ShortenHeader, 0, 4) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'shn';
|
$info['fileformat'] = 'shn';
|
||||||
$info['audio']['dataformat'] = 'shn';
|
$info['audio']['dataformat'] = 'shn';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
|
||||||
$info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
|
$info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
|
||||||
|
|
||||||
$this->fseek($info['avdataend'] - 12);
|
$this->fseek($info['avdataend'] - 12);
|
||||||
$SeekTableSignatureTest = $this->fread(12);
|
$SeekTableSignatureTest = $this->fread(12);
|
||||||
$info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
|
$info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
|
||||||
if ($info['shn']['seektable']['present']) {
|
if ($info['shn']['seektable']['present']) {
|
||||||
$info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
|
$info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
|
||||||
$info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length'];
|
$info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length'];
|
||||||
$this->fseek($info['shn']['seektable']['offset']);
|
$this->fseek($info['shn']['seektable']['offset']);
|
||||||
$SeekTableMagic = $this->fread(4);
|
$SeekTableMagic = $this->fread(4);
|
||||||
$magic = 'SEEK';
|
$magic = 'SEEK';
|
||||||
if ($SeekTableMagic != $magic) {
|
if ($SeekTableMagic != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['shn']['seektable']['offset'].', found "'.getid3_lib::PrintHexBytes($SeekTableMagic).'"';
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// typedef struct tag_TSeekEntry
|
|
||||||
// {
|
|
||||||
// unsigned long SampleNumber;
|
|
||||||
// unsigned long SHNFileByteOffset;
|
|
||||||
// unsigned long SHNLastBufferReadPosition;
|
|
||||||
// unsigned short SHNByteGet;
|
|
||||||
// unsigned short SHNBufferOffset;
|
|
||||||
// unsigned short SHNFileBitOffset;
|
|
||||||
// unsigned long SHNGBuffer;
|
|
||||||
// unsigned short SHNBitShift;
|
|
||||||
// long CBuf0[3];
|
|
||||||
// long CBuf1[3];
|
|
||||||
// long Offset0[4];
|
|
||||||
// long Offset1[4];
|
|
||||||
// }TSeekEntry;
|
|
||||||
|
|
||||||
$SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16);
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['shn']['seektable']['offset'].', found "'.getid3_lib::PrintHexBytes($SeekTableMagic).'"');
|
||||||
$info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
|
return false;
|
||||||
//$info['shn']['seektable']['entries'] = array();
|
|
||||||
//$SeekTableOffset = 0;
|
|
||||||
//for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) {
|
|
||||||
// $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
|
||||||
// $SeekTableOffset += 4;
|
|
||||||
// $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
|
||||||
// $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;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $info['shn']['seektable']['entries'][] = $SeekTableEntry;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
} else {
|
||||||
$info['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GETID3_OS_ISWINDOWS) {
|
// typedef struct tag_TSeekEntry
|
||||||
$RequiredFiles = ['shorten.exe', 'cygwin1.dll', 'head.exe'];
|
// {
|
||||||
foreach ($RequiredFiles as $required_file) {
|
// unsigned long SampleNumber;
|
||||||
if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
|
// unsigned long SHNFileByteOffset;
|
||||||
$info['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist';
|
// unsigned long SHNLastBufferReadPosition;
|
||||||
return false;
|
// unsigned short SHNByteGet;
|
||||||
}
|
// unsigned short SHNBufferOffset;
|
||||||
}
|
// unsigned short SHNFileBitOffset;
|
||||||
$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$info['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64';
|
// unsigned long SHNGBuffer;
|
||||||
$commandline = str_replace('/', '\\', $commandline);
|
// unsigned short SHNBitShift;
|
||||||
} else {
|
// long CBuf0[3];
|
||||||
static $shorten_present;
|
// long CBuf1[3];
|
||||||
if (!isset($shorten_present)) {
|
// long Offset0[4];
|
||||||
$shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
|
// long Offset1[4];
|
||||||
}
|
// }TSeekEntry;
|
||||||
if (!$shorten_present) {
|
|
||||||
$info['error'][] = 'shorten binary was not found in path or /usr/local/bin';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($info['filenamepath']).' - | head -c 64';
|
|
||||||
}
|
|
||||||
|
|
||||||
$output = `$commandline`;
|
$SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16);
|
||||||
|
$info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
|
||||||
|
//$info['shn']['seektable']['entries'] = array();
|
||||||
|
//$SeekTableOffset = 0;
|
||||||
|
//for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) {
|
||||||
|
// $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||||
|
// $SeekTableOffset += 4;
|
||||||
|
// $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
|
||||||
|
// $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;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $info['shn']['seektable']['entries'][] = $SeekTableEntry;
|
||||||
|
//}
|
||||||
|
|
||||||
if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
|
}
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
|
||||||
|
|
||||||
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
|
}
|
||||||
$DecodedWAVFORMATEX = getid3_riff::parseWAVEFORMATex(substr($output, 20, $fmt_size));
|
|
||||||
$info['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
|
|
||||||
$info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
|
|
||||||
$info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
|
|
||||||
|
|
||||||
if (substr($output, 20 + $fmt_size, 4) == 'data') {
|
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||||
$info['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
|
$this->error('PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files');
|
||||||
} else {
|
return false;
|
||||||
$info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8;
|
if (GETID3_OS_ISWINDOWS) {
|
||||||
} else {
|
|
||||||
$info['error'][] = 'shorten failed to decode file to WAV for parsing';
|
$RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
|
||||||
return false;
|
foreach ($RequiredFiles as $required_file) {
|
||||||
}
|
if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
|
||||||
|
$this->error(GETID3_HELPERAPPSDIR.$required_file.' does not exist');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$info['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64';
|
||||||
|
$commandline = str_replace('/', '\\', $commandline);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
static $shorten_present;
|
||||||
|
if (!isset($shorten_present)) {
|
||||||
|
$shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
|
||||||
|
}
|
||||||
|
if (!$shorten_present) {
|
||||||
|
$this->error('shorten binary was not found in path or /usr/local/bin');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($info['filenamepath']).' - | head -c 64';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = `$commandline`;
|
||||||
|
|
||||||
|
if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
|
||||||
|
|
||||||
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||||
|
|
||||||
|
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
|
||||||
|
$DecodedWAVFORMATEX = getid3_riff::parseWAVEFORMATex(substr($output, 20, $fmt_size));
|
||||||
|
$info['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
|
||||||
|
$info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
|
||||||
|
$info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
|
||||||
|
|
||||||
|
if (substr($output, 20 + $fmt_size, 4) == 'data') {
|
||||||
|
|
||||||
|
$info['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->error('shorten failed to decode DATA chunk to expected location, cannot determine playtime');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->error('shorten failed to decode file to WAV for parsing');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,90 +18,90 @@
|
||||||
class getid3_tta extends getid3_handler
|
class getid3_tta extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'tta';
|
$info['fileformat'] = 'tta';
|
||||||
$info['audio']['dataformat'] = 'tta';
|
$info['audio']['dataformat'] = 'tta';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$ttaheader = $this->fread(26);
|
$ttaheader = $this->fread(26);
|
||||||
|
|
||||||
$info['tta']['magic'] = substr($ttaheader, 0, 3);
|
$info['tta']['magic'] = substr($ttaheader, 0, 3);
|
||||||
$magic = 'TTA';
|
$magic = 'TTA';
|
||||||
if ($info['tta']['magic'] != $magic) {
|
if ($info['tta']['magic'] != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['tta']['magic']).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['tta']['magic']).'"');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['audio']);
|
unset($info['audio']);
|
||||||
unset($info['tta']);
|
unset($info['tta']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($ttaheader{3}) {
|
switch ($ttaheader{3}) {
|
||||||
case "\x01": // TTA v1.x
|
case "\x01": // TTA v1.x
|
||||||
case "\x02": // TTA v1.x
|
case "\x02": // TTA v1.x
|
||||||
case "\x03": // 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."
|
// "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
|
||||||
$info['tta']['major_version'] = 1;
|
$info['tta']['major_version'] = 1;
|
||||||
$info['avdataoffset'] += 16;
|
$info['avdataoffset'] += 16;
|
||||||
|
|
||||||
$info['tta']['compression_level'] = ord($ttaheader{3});
|
$info['tta']['compression_level'] = ord($ttaheader{3});
|
||||||
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
||||||
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||||
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
|
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
|
||||||
$info['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
$info['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
||||||
|
|
||||||
$info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level'];
|
$info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level'];
|
||||||
$info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate'];
|
$info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '2': // TTA v2.x
|
case '2': // TTA v2.x
|
||||||
// "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
|
// "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
|
||||||
$info['tta']['major_version'] = 2;
|
$info['tta']['major_version'] = 2;
|
||||||
$info['avdataoffset'] += 20;
|
$info['avdataoffset'] += 20;
|
||||||
|
|
||||||
$info['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
$info['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
|
||||||
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||||
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||||
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
|
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2));
|
||||||
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4));
|
||||||
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
|
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4));
|
||||||
|
|
||||||
$info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level'];
|
$info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level'];
|
||||||
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '1': // TTA v3.x
|
case '1': // TTA v3.x
|
||||||
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
|
// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
|
||||||
$info['tta']['major_version'] = 3;
|
$info['tta']['major_version'] = 3;
|
||||||
$info['avdataoffset'] += 26;
|
$info['avdataoffset'] += 26;
|
||||||
|
|
||||||
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::wFormatTagLookup()
|
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::wFormatTagLookup()
|
||||||
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
|
||||||
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
|
||||||
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
|
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
|
||||||
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
|
$info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4));
|
||||||
$info['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
|
$info['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
|
||||||
$info['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
|
$info['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4));
|
||||||
|
|
||||||
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
$info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$info['error'][] = 'This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3};
|
$this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3});
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['audio']['encoder'] = 'TTA v'.$info['tta']['major_version'];
|
$info['audio']['encoder'] = 'TTA v'.$info['tta']['major_version'];
|
||||||
$info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample'];
|
$info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample'];
|
||||||
$info['audio']['sample_rate'] = $info['tta']['sample_rate'];
|
$info['audio']['sample_rate'] = $info['tta']['sample_rate'];
|
||||||
$info['audio']['channels'] = $info['tta']['channels'];
|
$info['audio']['channels'] = $info['tta']['channels'];
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,189 +18,188 @@
|
||||||
class getid3_voc extends getid3_handler
|
class getid3_voc extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$OriginalAVdataOffset = $info['avdataoffset'];
|
$OriginalAVdataOffset = $info['avdataoffset'];
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$VOCheader = $this->fread(26);
|
$VOCheader = $this->fread(26);
|
||||||
|
|
||||||
$magic = 'Creative Voice File';
|
$magic = 'Creative Voice File';
|
||||||
if (substr($VOCheader, 0, 19) != $magic) {
|
if (substr($VOCheader, 0, 19) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($VOCheader, 0, 19)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($VOCheader, 0, 19)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcuts
|
// shortcuts
|
||||||
$thisfile_audio = &$info['audio'];
|
$thisfile_audio = &$info['audio'];
|
||||||
$info['voc'] = [];
|
$info['voc'] = array();
|
||||||
$thisfile_voc = &$info['voc'];
|
$thisfile_voc = &$info['voc'];
|
||||||
|
|
||||||
$info['fileformat'] = 'voc';
|
$info['fileformat'] = 'voc';
|
||||||
$thisfile_audio['dataformat'] = 'voc';
|
$thisfile_audio['dataformat'] = 'voc';
|
||||||
$thisfile_audio['bitrate_mode'] = 'cbr';
|
$thisfile_audio['bitrate_mode'] = 'cbr';
|
||||||
$thisfile_audio['lossless'] = true;
|
$thisfile_audio['lossless'] = true;
|
||||||
$thisfile_audio['channels'] = 1; // might be overriden below
|
$thisfile_audio['channels'] = 1; // might be overriden below
|
||||||
$thisfile_audio['bits_per_sample'] = 8; // might be overriden below
|
$thisfile_audio['bits_per_sample'] = 8; // might be overriden below
|
||||||
|
|
||||||
// byte # Description
|
// byte # Description
|
||||||
// ------ ------------------------------------------
|
// ------ ------------------------------------------
|
||||||
// 00-12 'Creative Voice File'
|
// 00-12 'Creative Voice File'
|
||||||
// 13 1A (eof to abort printing of file)
|
// 13 1A (eof to abort printing of file)
|
||||||
// 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
|
// 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)
|
// 16-17 Version number (minor,major) (VOC-HDR puts 0A 01)
|
||||||
// 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
|
// 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
|
||||||
|
|
||||||
$thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
|
$thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
|
||||||
$thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
|
$thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
|
||||||
$thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
|
$thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$BlockOffset = $this->ftell();
|
|
||||||
$BlockData = $this->fread(4);
|
|
||||||
$BlockType = ord($BlockData{0});
|
|
||||||
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
|
|
||||||
$ThisBlock = [];
|
|
||||||
|
|
||||||
getid3_lib::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1);
|
$BlockOffset = $this->ftell();
|
||||||
switch ($BlockType) {
|
$BlockData = $this->fread(4);
|
||||||
case 0: // Terminator
|
$BlockType = ord($BlockData{0});
|
||||||
// do nothing, we'll break out of the loop down below
|
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
|
||||||
break;
|
$ThisBlock = array();
|
||||||
|
|
||||||
case 1: // Sound data
|
getid3_lib::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1);
|
||||||
$BlockData .= $this->fread(2);
|
switch ($BlockType) {
|
||||||
if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
|
case 0: // Terminator
|
||||||
$info['avdataoffset'] = $this->ftell();
|
// do nothing, we'll break out of the loop down below
|
||||||
}
|
break;
|
||||||
$this->fseek($BlockSize - 2, SEEK_CUR);
|
|
||||||
|
|
||||||
$ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
|
case 1: // Sound data
|
||||||
$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
|
$BlockData .= $this->fread(2);
|
||||||
|
if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
|
||||||
|
$info['avdataoffset'] = $this->ftell();
|
||||||
|
}
|
||||||
|
$this->fseek($BlockSize - 2, SEEK_CUR);
|
||||||
|
|
||||||
$ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
|
$ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
|
||||||
if ($ThisBlock['compression_type'] <= 3) {
|
$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
|
||||||
$thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
|
$ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
|
||||||
if (empty($thisfile_audio['sample_rate'])) {
|
if ($ThisBlock['compression_type'] <= 3) {
|
||||||
// SR byte = 256 - (1000000 / sample_rate)
|
$thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
|
||||||
$thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // Sound continue
|
// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
|
||||||
case 3: // Silence
|
if (empty($thisfile_audio['sample_rate'])) {
|
||||||
case 4: // Marker
|
// SR byte = 256 - (1000000 / sample_rate)
|
||||||
case 6: // Repeat
|
$thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
|
||||||
case 7: // End repeat
|
}
|
||||||
// nothing useful, just skip
|
break;
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8: // Extended
|
case 2: // Sound continue
|
||||||
$BlockData .= $this->fread(4);
|
case 3: // Silence
|
||||||
|
case 4: // Marker
|
||||||
|
case 6: // Repeat
|
||||||
|
case 7: // End repeat
|
||||||
|
// nothing useful, just skip
|
||||||
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
|
break;
|
||||||
|
|
||||||
//00-01 Time Constant:
|
case 8: // Extended
|
||||||
// Mono: 65536 - (256000000 / sample_rate)
|
$BlockData .= $this->fread(4);
|
||||||
// Stereo: 65536 - (256000000 / (sample_rate * 2))
|
|
||||||
$ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
|
|
||||||
$ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
|
|
||||||
$ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
|
|
||||||
|
|
||||||
$thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
|
//00-01 Time Constant:
|
||||||
$thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
|
// Mono: 65536 - (256000000 / sample_rate)
|
||||||
break;
|
// Stereo: 65536 - (256000000 / (sample_rate * 2))
|
||||||
|
$ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
|
||||||
|
$ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
|
||||||
|
$ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
|
||||||
|
|
||||||
case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
|
$thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1);
|
||||||
$BlockData .= $this->fread(12);
|
$thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
|
||||||
if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
|
break;
|
||||||
$info['avdataoffset'] = $this->ftell();
|
|
||||||
}
|
|
||||||
$this->fseek($BlockSize - 12, SEEK_CUR);
|
|
||||||
|
|
||||||
$ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
|
||||||
$ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
|
$BlockData .= $this->fread(12);
|
||||||
$ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
|
if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
|
||||||
$ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
|
$info['avdataoffset'] = $this->ftell();
|
||||||
|
}
|
||||||
|
$this->fseek($BlockSize - 12, SEEK_CUR);
|
||||||
|
|
||||||
$ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
|
$ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
|
||||||
if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
|
$ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
|
||||||
$thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
|
$ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1));
|
||||||
}
|
$ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
|
||||||
|
|
||||||
$thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
|
$ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
|
||||||
$thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
|
if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
|
||||||
$thisfile_audio['channels'] = $ThisBlock['channels'];
|
$thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
|
||||||
break;
|
}
|
||||||
|
|
||||||
default:
|
$thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
|
||||||
$info['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
|
$thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
|
||||||
$this->fseek($BlockSize, SEEK_CUR);
|
$thisfile_audio['channels'] = $ThisBlock['channels'];
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($ThisBlock)) {
|
default:
|
||||||
$ThisBlock['block_offset'] = $BlockOffset;
|
$this->warning('Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset);
|
||||||
$ThisBlock['block_size'] = $BlockSize;
|
$this->fseek($BlockSize, SEEK_CUR);
|
||||||
$ThisBlock['block_type_id'] = $BlockType;
|
break;
|
||||||
$thisfile_voc['blocks'][] = $ThisBlock;
|
}
|
||||||
}
|
|
||||||
} while (!feof($this->getid3->fp) && ($BlockType != 0));
|
|
||||||
|
|
||||||
// Terminator block doesn't have size field, so seek back 3 spaces
|
if (!empty($ThisBlock)) {
|
||||||
$this->fseek(-3, SEEK_CUR);
|
$ThisBlock['block_offset'] = $BlockOffset;
|
||||||
|
$ThisBlock['block_size'] = $BlockSize;
|
||||||
|
$ThisBlock['block_type_id'] = $BlockType;
|
||||||
|
$thisfile_voc['blocks'][] = $ThisBlock;
|
||||||
|
}
|
||||||
|
|
||||||
ksort($thisfile_voc['blocktypes']);
|
} while (!feof($this->getid3->fp) && ($BlockType != 0));
|
||||||
|
|
||||||
if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
|
// Terminator block doesn't have size field, so seek back 3 spaces
|
||||||
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
|
$this->fseek(-3, SEEK_CUR);
|
||||||
$thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
ksort($thisfile_voc['blocktypes']);
|
||||||
}
|
|
||||||
|
|
||||||
public function VOCcompressionTypeLookup($index)
|
if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
|
||||||
{
|
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
|
||||||
static $VOCcompressionTypeLookup = [
|
$thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
0 => '8-bit',
|
}
|
||||||
1 => '4-bit',
|
|
||||||
2 => '2.6-bit',
|
|
||||||
3 => '2-bit'
|
|
||||||
];
|
|
||||||
return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function VOCwFormatLookup($index)
|
return true;
|
||||||
{
|
}
|
||||||
static $VOCwFormatLookup = [
|
|
||||||
0x0000 => '8-bit unsigned PCM',
|
public function VOCcompressionTypeLookup($index) {
|
||||||
0x0001 => 'Creative 8-bit to 4-bit ADPCM',
|
static $VOCcompressionTypeLookup = array(
|
||||||
0x0002 => 'Creative 8-bit to 3-bit ADPCM',
|
0 => '8-bit',
|
||||||
0x0003 => 'Creative 8-bit to 2-bit ADPCM',
|
1 => '4-bit',
|
||||||
0x0004 => '16-bit signed PCM',
|
2 => '2.6-bit',
|
||||||
0x0006 => 'CCITT a-Law',
|
3 => '2-bit'
|
||||||
0x0007 => 'CCITT u-Law',
|
);
|
||||||
0x2000 => 'Creative 16-bit to 4-bit ADPCM'
|
return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
|
||||||
];
|
}
|
||||||
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
|
||||||
}
|
public function VOCwFormatLookup($index) {
|
||||||
|
static $VOCwFormatLookup = array(
|
||||||
|
0x0000 => '8-bit unsigned PCM',
|
||||||
|
0x0001 => 'Creative 8-bit to 4-bit ADPCM',
|
||||||
|
0x0002 => 'Creative 8-bit to 3-bit ADPCM',
|
||||||
|
0x0003 => 'Creative 8-bit to 2-bit ADPCM',
|
||||||
|
0x0004 => '16-bit signed PCM',
|
||||||
|
0x0006 => 'CCITT a-Law',
|
||||||
|
0x0007 => 'CCITT u-Law',
|
||||||
|
0x2000 => 'Creative 16-bit to 4-bit ADPCM'
|
||||||
|
);
|
||||||
|
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function VOCwFormatActualBitsPerSampleLookup($index) {
|
||||||
|
static $VOCwFormatLookup = array(
|
||||||
|
0x0000 => 8,
|
||||||
|
0x0001 => 4,
|
||||||
|
0x0002 => 3,
|
||||||
|
0x0003 => 2,
|
||||||
|
0x0004 => 16,
|
||||||
|
0x0006 => 8,
|
||||||
|
0x0007 => 8,
|
||||||
|
0x2000 => 4
|
||||||
|
);
|
||||||
|
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
||||||
|
}
|
||||||
|
|
||||||
public function VOCwFormatActualBitsPerSampleLookup($index)
|
|
||||||
{
|
|
||||||
static $VOCwFormatLookup = [
|
|
||||||
0x0000 => 8,
|
|
||||||
0x0001 => 4,
|
|
||||||
0x0002 => 3,
|
|
||||||
0x0003 => 2,
|
|
||||||
0x0004 => 16,
|
|
||||||
0x0006 => 8,
|
|
||||||
0x0007 => 8,
|
|
||||||
0x2000 => 4
|
|
||||||
];
|
|
||||||
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,145 +17,144 @@
|
||||||
|
|
||||||
class getid3_vqf extends getid3_handler
|
class getid3_vqf extends getid3_handler
|
||||||
{
|
{
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
|
// 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
|
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
|
||||||
|
|
||||||
$info['fileformat'] = 'vqf';
|
$info['fileformat'] = 'vqf';
|
||||||
$info['audio']['dataformat'] = 'vqf';
|
$info['audio']['dataformat'] = 'vqf';
|
||||||
$info['audio']['bitrate_mode'] = 'cbr';
|
$info['audio']['bitrate_mode'] = 'cbr';
|
||||||
$info['audio']['lossless'] = false;
|
$info['audio']['lossless'] = false;
|
||||||
|
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['vqf']['raw'] = [];
|
$info['vqf']['raw'] = array();
|
||||||
$thisfile_vqf = &$info['vqf'];
|
$thisfile_vqf = &$info['vqf'];
|
||||||
$thisfile_vqf_raw = &$thisfile_vqf['raw'];
|
$thisfile_vqf_raw = &$thisfile_vqf['raw'];
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$VQFheaderData = $this->fread(16);
|
$VQFheaderData = $this->fread(16);
|
||||||
|
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
|
$thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
|
||||||
$magic = 'TWIN';
|
$magic = 'TWIN';
|
||||||
if ($thisfile_vqf_raw['header_tag'] != $magic) {
|
if ($thisfile_vqf_raw['header_tag'] != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_vqf_raw['header_tag']).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_vqf_raw['header_tag']).'"');
|
||||||
unset($info['vqf']);
|
unset($info['vqf']);
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
|
$thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
|
||||||
$offset += 8;
|
$offset += 8;
|
||||||
$thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
|
$thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
|
|
||||||
while ($this->ftell() < $info['avdataend']) {
|
while ($this->ftell() < $info['avdataend']) {
|
||||||
$ChunkBaseOffset = $this->ftell();
|
|
||||||
$chunkoffset = 0;
|
|
||||||
$ChunkData = $this->fread(8);
|
|
||||||
$ChunkName = substr($ChunkData, $chunkoffset, 4);
|
|
||||||
if ($ChunkName == 'DATA') {
|
|
||||||
$info['avdataoffset'] = $ChunkBaseOffset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$chunkoffset += 4;
|
|
||||||
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
|
||||||
$chunkoffset += 4;
|
|
||||||
if ($ChunkSize > ($info['avdataend'] - $this->ftell())) {
|
|
||||||
$info['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($ChunkSize > 0) {
|
|
||||||
$ChunkData .= $this->fread($ChunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($ChunkName) {
|
$ChunkBaseOffset = $this->ftell();
|
||||||
case 'COMM':
|
$chunkoffset = 0;
|
||||||
// shortcut
|
$ChunkData = $this->fread(8);
|
||||||
$thisfile_vqf['COMM'] = [];
|
$ChunkName = substr($ChunkData, $chunkoffset, 4);
|
||||||
$thisfile_vqf_COMM = &$thisfile_vqf['COMM'];
|
if ($ChunkName == 'DATA') {
|
||||||
|
$info['avdataoffset'] = $ChunkBaseOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$chunkoffset += 4;
|
||||||
|
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||||
|
$chunkoffset += 4;
|
||||||
|
if ($ChunkSize > ($info['avdataend'] - $this->ftell())) {
|
||||||
|
$this->error('Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($ChunkSize > 0) {
|
||||||
|
$ChunkData .= $this->fread($ChunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
$thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
switch ($ChunkName) {
|
||||||
$chunkoffset += 4;
|
case 'COMM':
|
||||||
$thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
// shortcut
|
||||||
$chunkoffset += 4;
|
$thisfile_vqf['COMM'] = array();
|
||||||
$thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
$thisfile_vqf_COMM = &$thisfile_vqf['COMM'];
|
||||||
$chunkoffset += 4;
|
|
||||||
$thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
|
||||||
$chunkoffset += 4;
|
|
||||||
|
|
||||||
$info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
|
$thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||||
$info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
|
$chunkoffset += 4;
|
||||||
$info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
|
$thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
|
||||||
$info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate']/1000);
|
$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;
|
||||||
|
|
||||||
if ($info['audio']['bitrate'] == 0) {
|
$info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
|
||||||
$info['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
|
$info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
|
||||||
return false;
|
$info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
|
||||||
}
|
$info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate']/1000);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'NAME':
|
if ($info['audio']['bitrate'] == 0) {
|
||||||
case 'AUTH':
|
$this->error('Corrupt VQF file: bitrate_audio == zero');
|
||||||
case '(c) ':
|
return false;
|
||||||
case 'FILE':
|
}
|
||||||
case 'COMT':
|
break;
|
||||||
case 'ALBM':
|
|
||||||
$thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DSIZ':
|
case 'NAME':
|
||||||
$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
|
case 'AUTH':
|
||||||
break;
|
case '(c) ':
|
||||||
|
case 'FILE':
|
||||||
|
case 'COMT':
|
||||||
|
case 'ALBM':
|
||||||
|
$thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case 'DSIZ':
|
||||||
$info['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
|
$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
default:
|
||||||
|
$this->warning('Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'))))) {
|
$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate'];
|
||||||
switch ($thisfile_vqf['DSIZ']) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
$info['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0';
|
|
||||||
$info['audio']['encoder'] = 'Ahead Nero';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'))))) {
|
||||||
$info['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'));
|
switch ($thisfile_vqf['DSIZ']) {
|
||||||
break;
|
case 0:
|
||||||
}
|
case 1:
|
||||||
}
|
$this->warning('Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0');
|
||||||
|
$info['audio']['encoder'] = 'Ahead Nero';
|
||||||
|
break;
|
||||||
|
|
||||||
return true;
|
default:
|
||||||
}
|
$this->warning('Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($info['avdataend'] - $info['avdataoffset'] - strlen('DATA')));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function VQFchannelFrequencyLookup($frequencyid)
|
return true;
|
||||||
{
|
}
|
||||||
static $VQFchannelFrequencyLookup = [
|
|
||||||
11 => 11025,
|
public function VQFchannelFrequencyLookup($frequencyid) {
|
||||||
22 => 22050,
|
static $VQFchannelFrequencyLookup = array(
|
||||||
44 => 44100
|
11 => 11025,
|
||||||
];
|
22 => 22050,
|
||||||
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
|
44 => 44100
|
||||||
}
|
);
|
||||||
|
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function VQFcommentNiceNameLookup($shortname) {
|
||||||
|
static $VQFcommentNiceNameLookup = array(
|
||||||
|
'NAME' => 'title',
|
||||||
|
'AUTH' => 'artist',
|
||||||
|
'(c) ' => 'copyright',
|
||||||
|
'FILE' => 'filename',
|
||||||
|
'COMT' => 'comment',
|
||||||
|
'ALBM' => 'album'
|
||||||
|
);
|
||||||
|
return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
|
||||||
|
}
|
||||||
|
|
||||||
public function VQFcommentNiceNameLookup($shortname)
|
|
||||||
{
|
|
||||||
static $VQFcommentNiceNameLookup = [
|
|
||||||
'NAME' => 'title',
|
|
||||||
'AUTH' => 'artist',
|
|
||||||
'(c) ' => 'copyright',
|
|
||||||
'FILE' => 'filename',
|
|
||||||
'COMT' => 'comment',
|
|
||||||
'ALBM' => 'album'
|
|
||||||
];
|
|
||||||
return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,371 +18,381 @@
|
||||||
class getid3_wavpack extends getid3_handler
|
class getid3_wavpack extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
$wavpackheader = $this->fread(32);
|
|
||||||
|
|
||||||
if ($this->ftell() >= $info['avdataend']) {
|
$wavpackheader = $this->fread(32);
|
||||||
break;
|
|
||||||
} elseif (feof($this->getid3->fp)) {
|
|
||||||
break;
|
|
||||||
} elseif (isset($info['wavpack']['blockheader']['total_samples']) &&
|
|
||||||
isset($info['wavpack']['blockheader']['block_samples']) &&
|
|
||||||
($info['wavpack']['blockheader']['total_samples'] > 0) &&
|
|
||||||
($info['wavpack']['blockheader']['block_samples'] > 0) &&
|
|
||||||
(!isset($info['wavpack']['riff_trailer_size']) || ($info['wavpack']['riff_trailer_size'] <= 0)) &&
|
|
||||||
((isset($info['wavpack']['config_flags']['md5_checksum']) && ($info['wavpack']['config_flags']['md5_checksum'] === false)) || !empty($info['md5_data_source']))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$blockheader_offset = $this->ftell() - 32;
|
if ($this->ftell() >= $info['avdataend']) {
|
||||||
$blockheader_magic = substr($wavpackheader, 0, 4);
|
break;
|
||||||
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
|
} elseif (feof($this->getid3->fp)) {
|
||||||
|
break;
|
||||||
|
} elseif (
|
||||||
|
isset($info['wavpack']['blockheader']['total_samples']) &&
|
||||||
|
isset($info['wavpack']['blockheader']['block_samples']) &&
|
||||||
|
($info['wavpack']['blockheader']['total_samples'] > 0) &&
|
||||||
|
($info['wavpack']['blockheader']['block_samples'] > 0) &&
|
||||||
|
(!isset($info['wavpack']['riff_trailer_size']) || ($info['wavpack']['riff_trailer_size'] <= 0)) &&
|
||||||
|
((isset($info['wavpack']['config_flags']['md5_checksum']) && ($info['wavpack']['config_flags']['md5_checksum'] === false)) || !empty($info['md5_data_source']))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$magic = 'wvpk';
|
$blockheader_offset = $this->ftell() - 32;
|
||||||
if ($blockheader_magic != $magic) {
|
$blockheader_magic = substr($wavpackheader, 0, 4);
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$blockheader_offset.', found "'.getid3_lib::PrintHexBytes($blockheader_magic).'"';
|
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
|
||||||
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
|
||||||
case 'wavpack':
|
|
||||||
case 'wvc':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
unset($info['fileformat']);
|
|
||||||
unset($info['audio']);
|
|
||||||
unset($info['wavpack']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($info['wavpack']['blockheader']['block_samples']) ||
|
$magic = 'wvpk';
|
||||||
empty($info['wavpack']['blockheader']['total_samples']) ||
|
if ($blockheader_magic != $magic) {
|
||||||
($info['wavpack']['blockheader']['block_samples'] <= 0) ||
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$blockheader_offset.', found "'.getid3_lib::PrintHexBytes($blockheader_magic).'"');
|
||||||
($info['wavpack']['blockheader']['total_samples'] <= 0)) {
|
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
||||||
// Also, it is possible that the first block might not have
|
case 'wavpack':
|
||||||
// any samples (block_samples == 0) and in this case you should skip blocks
|
case 'wvc':
|
||||||
// until you find one with samples because the other information (like
|
break;
|
||||||
// total_samples) are not guaranteed to be correct until (block_samples > 0)
|
default:
|
||||||
|
unset($info['fileformat']);
|
||||||
|
unset($info['audio']);
|
||||||
|
unset($info['wavpack']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, I have defined a format for files in which the length is not known
|
if (empty($info['wavpack']['blockheader']['block_samples']) ||
|
||||||
// (for example when raw files are created using pipes). In these cases
|
empty($info['wavpack']['blockheader']['total_samples']) ||
|
||||||
// total_samples will be -1 and you must seek to the final block to determine
|
($info['wavpack']['blockheader']['block_samples'] <= 0) ||
|
||||||
// the total number of samples.
|
($info['wavpack']['blockheader']['total_samples'] <= 0)) {
|
||||||
|
// Also, it is possible that the first block might not have
|
||||||
|
// any samples (block_samples == 0) and in this case you should skip blocks
|
||||||
|
// until you find one with samples because the other information (like
|
||||||
|
// total_samples) are not guaranteed to be correct until (block_samples > 0)
|
||||||
|
|
||||||
|
// Finally, I have defined a format for files in which the length is not known
|
||||||
|
// (for example when raw files are created using pipes). In these cases
|
||||||
|
// total_samples will be -1 and you must seek to the final block to determine
|
||||||
|
// the total number of samples.
|
||||||
|
|
||||||
|
|
||||||
$info['audio']['dataformat'] = 'wavpack';
|
$info['audio']['dataformat'] = 'wavpack';
|
||||||
$info['fileformat'] = 'wavpack';
|
$info['fileformat'] = 'wavpack';
|
||||||
$info['audio']['lossless'] = true;
|
$info['audio']['lossless'] = true;
|
||||||
$info['audio']['bitrate_mode'] = 'vbr';
|
$info['audio']['bitrate_mode'] = 'vbr';
|
||||||
|
|
||||||
$info['wavpack']['blockheader']['offset'] = $blockheader_offset;
|
$info['wavpack']['blockheader']['offset'] = $blockheader_offset;
|
||||||
$info['wavpack']['blockheader']['magic'] = $blockheader_magic;
|
$info['wavpack']['blockheader']['magic'] = $blockheader_magic;
|
||||||
$info['wavpack']['blockheader']['size'] = $blockheader_size;
|
$info['wavpack']['blockheader']['size'] = $blockheader_size;
|
||||||
|
|
||||||
if ($info['wavpack']['blockheader']['size'] >= 0x100000) {
|
if ($info['wavpack']['blockheader']['size'] >= 0x100000) {
|
||||||
$info['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$info['wavpack']['blockheader']['size'].'" at offset '.$info['wavpack']['blockheader']['offset'];
|
$this->error('Expecting WavPack block size less than "0x100000", found "'.$info['wavpack']['blockheader']['size'].'" at offset '.$info['wavpack']['blockheader']['offset']);
|
||||||
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
||||||
case 'wavpack':
|
case 'wavpack':
|
||||||
case 'wvc':
|
case 'wvc':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['audio']);
|
unset($info['audio']);
|
||||||
unset($info['wavpack']);
|
unset($info['wavpack']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
|
$info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
|
||||||
$info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
|
$info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
|
||||||
|
|
||||||
if (($info['wavpack']['blockheader']['major_version'] != 4) ||
|
if (($info['wavpack']['blockheader']['major_version'] != 4) ||
|
||||||
(($info['wavpack']['blockheader']['minor_version'] < 4) &&
|
(($info['wavpack']['blockheader']['minor_version'] < 4) &&
|
||||||
($info['wavpack']['blockheader']['minor_version'] > 16))) {
|
($info['wavpack']['blockheader']['minor_version'] > 16))) {
|
||||||
$info['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$info['wavpack']['blockheader']['major_version'].'.'.$info['wavpack']['blockheader']['minor_version'].'" at offset '.$info['wavpack']['blockheader']['offset'];
|
$this->error('Expecting WavPack version between "4.2" and "4.16", found version "'.$info['wavpack']['blockheader']['major_version'].'.'.$info['wavpack']['blockheader']['minor_version'].'" at offset '.$info['wavpack']['blockheader']['offset']);
|
||||||
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
|
||||||
case 'wavpack':
|
case 'wavpack':
|
||||||
case 'wvc':
|
case 'wvc':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['audio']);
|
unset($info['audio']);
|
||||||
unset($info['wavpack']);
|
unset($info['wavpack']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
|
$info['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
|
||||||
$info['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
|
$info['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
|
||||||
$info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
|
$info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
|
||||||
$info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
|
$info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
|
||||||
$info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
|
$info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
|
||||||
$info['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
|
$info['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4));
|
||||||
$info['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
|
$info['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4));
|
||||||
|
|
||||||
$info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x00000003);
|
$info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x00000003);
|
||||||
$info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000004);
|
$info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000004);
|
||||||
$info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000008);
|
$info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000008);
|
||||||
$info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000010);
|
$info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000010);
|
||||||
$info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000020);
|
$info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000020);
|
||||||
$info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000040);
|
$info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000040);
|
||||||
$info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000080);
|
$info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000080);
|
||||||
$info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000100);
|
$info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000100);
|
||||||
$info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000200);
|
$info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000200);
|
||||||
$info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000400);
|
$info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000400);
|
||||||
$info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000800);
|
$info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00000800);
|
||||||
$info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00001000);
|
$info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x00001000);
|
||||||
|
|
||||||
$info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid'];
|
$info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid'];
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!feof($this->getid3->fp) && ($this->ftell() < ($blockheader_offset + $blockheader_size + 8))) {
|
while (!feof($this->getid3->fp) && ($this->ftell() < ($blockheader_offset + $blockheader_size + 8))) {
|
||||||
$metablock = ['offset'=>$this->ftell()];
|
|
||||||
$metablockheader = $this->fread(2);
|
|
||||||
if (feof($this->getid3->fp)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$metablock['id'] = ord($metablockheader{0});
|
|
||||||
$metablock['function_id'] = ($metablock['id'] & 0x3F);
|
|
||||||
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
|
|
||||||
|
|
||||||
// The 0x20 bit in the id of the meta subblocks (which is defined as
|
$metablock = array('offset'=>$this->ftell());
|
||||||
// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
|
$metablockheader = $this->fread(2);
|
||||||
// if a decoder encounters an id that it does not know about, it uses
|
if (feof($this->getid3->fp)) {
|
||||||
// that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set
|
break;
|
||||||
// then the decoder simply ignores the metadata, but if it is zero
|
}
|
||||||
// then the decoder should quit because it means that an understanding
|
$metablock['id'] = ord($metablockheader{0});
|
||||||
// of the metadata is required to correctly decode the audio.
|
$metablock['function_id'] = ($metablock['id'] & 0x3F);
|
||||||
$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
|
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
|
||||||
|
|
||||||
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
|
// The 0x20 bit in the id of the meta subblocks (which is defined as
|
||||||
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
|
// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
|
||||||
if ($metablock['large_block']) {
|
// if a decoder encounters an id that it does not know about, it uses
|
||||||
$metablockheader .= $this->fread(2);
|
// 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
|
||||||
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
|
// then the decoder should quit because it means that an understanding
|
||||||
$metablock['data'] = null;
|
// of the metadata is required to correctly decode the audio.
|
||||||
|
$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
|
||||||
|
|
||||||
if ($metablock['size'] > 0) {
|
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
|
||||||
switch ($metablock['function_id']) {
|
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
|
||||||
case 0x21: // ID_RIFF_HEADER
|
if ($metablock['large_block']) {
|
||||||
case 0x22: // ID_RIFF_TRAILER
|
$metablockheader .= $this->fread(2);
|
||||||
case 0x23: // ID_REPLAY_GAIN
|
}
|
||||||
case 0x24: // ID_CUESHEET
|
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
|
||||||
case 0x25: // ID_CONFIG_BLOCK
|
$metablock['data'] = null;
|
||||||
case 0x26: // ID_MD5_CHECKSUM
|
|
||||||
$metablock['data'] = $this->fread($metablock['size']);
|
|
||||||
|
|
||||||
if ($metablock['padded_data']) {
|
if ($metablock['size'] > 0) {
|
||||||
// padded to the nearest even byte
|
|
||||||
$metablock['size']--;
|
|
||||||
$metablock['data'] = substr($metablock['data'], 0, -1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x00: // ID_DUMMY
|
switch ($metablock['function_id']) {
|
||||||
case 0x01: // ID_ENCODER_INFO
|
case 0x21: // ID_RIFF_HEADER
|
||||||
case 0x02: // ID_DECORR_TERMS
|
case 0x22: // ID_RIFF_TRAILER
|
||||||
case 0x03: // ID_DECORR_WEIGHTS
|
case 0x23: // ID_REPLAY_GAIN
|
||||||
case 0x04: // ID_DECORR_SAMPLES
|
case 0x24: // ID_CUESHEET
|
||||||
case 0x05: // ID_ENTROPY_VARS
|
case 0x25: // ID_CONFIG_BLOCK
|
||||||
case 0x06: // ID_HYBRID_PROFILE
|
case 0x26: // ID_MD5_CHECKSUM
|
||||||
case 0x07: // ID_SHAPING_WEIGHTS
|
$metablock['data'] = $this->fread($metablock['size']);
|
||||||
case 0x08: // ID_FLOAT_INFO
|
|
||||||
case 0x09: // ID_INT32_INFO
|
|
||||||
case 0x0A: // ID_WV_BITSTREAM
|
|
||||||
case 0x0B: // ID_WVC_BITSTREAM
|
|
||||||
case 0x0C: // ID_WVX_BITSTREAM
|
|
||||||
case 0x0D: // ID_CHANNEL_INFO
|
|
||||||
$this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
if ($metablock['padded_data']) {
|
||||||
$info['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
|
// padded to the nearest even byte
|
||||||
$this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
|
$metablock['size']--;
|
||||||
break;
|
$metablock['data'] = substr($metablock['data'], 0, -1);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
switch ($metablock['function_id']) {
|
case 0x00: // ID_DUMMY
|
||||||
case 0x21: // ID_RIFF_HEADER
|
case 0x01: // ID_ENCODER_INFO
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
case 0x02: // ID_DECORR_TERMS
|
||||||
$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4));
|
case 0x03: // ID_DECORR_WEIGHTS
|
||||||
|
case 0x04: // ID_DECORR_SAMPLES
|
||||||
|
case 0x05: // ID_ENTROPY_VARS
|
||||||
|
case 0x06: // ID_HYBRID_PROFILE
|
||||||
|
case 0x07: // ID_SHAPING_WEIGHTS
|
||||||
|
case 0x08: // ID_FLOAT_INFO
|
||||||
|
case 0x09: // ID_INT32_INFO
|
||||||
|
case 0x0A: // ID_WV_BITSTREAM
|
||||||
|
case 0x0B: // ID_WVC_BITSTREAM
|
||||||
|
case 0x0C: // ID_WVX_BITSTREAM
|
||||||
|
case 0x0D: // ID_CHANNEL_INFO
|
||||||
|
$this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
|
||||||
|
break;
|
||||||
|
|
||||||
$getid3_temp = new getID3();
|
default:
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
$this->warning('Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']);
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
$this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
|
||||||
$getid3_riff->ParseRIFFdata($metablock['data']);
|
break;
|
||||||
$metablock['riff'] = $getid3_temp->info['riff'];
|
}
|
||||||
$info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec'];
|
|
||||||
unset($getid3_riff, $getid3_temp);
|
|
||||||
|
|
||||||
$metablock['riff']['original_filesize'] = $original_wav_filesize;
|
switch ($metablock['function_id']) {
|
||||||
$info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
|
case 0x21: // ID_RIFF_HEADER
|
||||||
$info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate'];
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||||
|
$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4));
|
||||||
|
|
||||||
// Safe RIFF header in case there's a RIFF footer later
|
$getid3_temp = new getID3();
|
||||||
$metablockRIFFheader = $metablock['data'];
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
break;
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
|
$getid3_riff->ParseRIFFdata($metablock['data']);
|
||||||
|
$metablock['riff'] = $getid3_temp->info['riff'];
|
||||||
|
$info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec'];
|
||||||
|
unset($getid3_riff, $getid3_temp);
|
||||||
|
|
||||||
|
$metablock['riff']['original_filesize'] = $original_wav_filesize;
|
||||||
|
$info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
|
||||||
|
$info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate'];
|
||||||
|
|
||||||
|
// Safe RIFF header in case there's a RIFF footer later
|
||||||
|
$metablockRIFFheader = $metablock['data'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x22: // ID_RIFF_TRAILER
|
case 0x22: // ID_RIFF_TRAILER
|
||||||
$metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
|
$metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
|
||||||
|
|
||||||
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
|
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
|
||||||
$getid3_temp = new getID3();
|
$getid3_temp = new getID3();
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
$getid3_temp->info['avdataend'] = $info['avdataend'];
|
||||||
//$getid3_temp->info['fileformat'] = 'riff';
|
//$getid3_temp->info['fileformat'] = 'riff';
|
||||||
$getid3_riff = new getid3_riff($getid3_temp);
|
$getid3_riff = new getid3_riff($getid3_temp);
|
||||||
$metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']);
|
$metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']);
|
||||||
|
|
||||||
if (!empty($metablock['riff']['INFO'])) {
|
if (!empty($metablock['riff']['INFO'])) {
|
||||||
getid3_riff::parseComments($metablock['riff']['INFO'], $metablock['comments']);
|
getid3_riff::parseComments($metablock['riff']['INFO'], $metablock['comments']);
|
||||||
$info['tags']['riff'] = $metablock['comments'];
|
$info['tags']['riff'] = $metablock['comments'];
|
||||||
}
|
}
|
||||||
unset($getid3_temp, $getid3_riff);
|
unset($getid3_temp, $getid3_riff);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x23: // ID_REPLAY_GAIN
|
case 0x23: // ID_REPLAY_GAIN
|
||||||
$info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
$this->warning('WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x24: // ID_CUESHEET
|
case 0x24: // ID_CUESHEET
|
||||||
$info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
|
$this->warning('WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x25: // ID_CONFIG_BLOCK
|
case 0x25: // ID_CONFIG_BLOCK
|
||||||
$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
|
$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
|
||||||
|
|
||||||
$metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats
|
$metablock['flags']['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']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode
|
||||||
$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
|
$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
|
||||||
$metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
|
$metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
|
||||||
$metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
|
$metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
|
||||||
$metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample
|
$metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample
|
||||||
$metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping
|
$metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping
|
||||||
$metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified
|
$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']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified
|
||||||
$metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source
|
$metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source
|
||||||
$metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
|
$metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
|
||||||
$metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
|
$metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
|
||||||
$metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression
|
$metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression
|
||||||
$metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode
|
$metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode
|
||||||
$metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet)
|
$metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet)
|
||||||
$metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
|
$metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
|
||||||
$metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
|
$metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
|
||||||
$metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode
|
$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']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints
|
||||||
$metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature
|
$metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature
|
||||||
$metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress %
|
$metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress %
|
||||||
|
|
||||||
$info['wavpack']['config_flags'] = $metablock['flags'];
|
$info['wavpack']['config_flags'] = $metablock['flags'];
|
||||||
|
|
||||||
|
|
||||||
$info['audio']['encoder_options'] = '';
|
$info['audio']['encoder_options'] = '';
|
||||||
if ($info['wavpack']['blockheader']['flags']['hybrid']) {
|
if ($info['wavpack']['blockheader']['flags']['hybrid']) {
|
||||||
$info['audio']['encoder_options'] .= ' -b???';
|
$info['audio']['encoder_options'] .= ' -b???';
|
||||||
}
|
}
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
|
||||||
$info['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : '');
|
$info['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : '');
|
||||||
if (!empty($info['audio']['encoder_options'])) {
|
if (!empty($info['audio']['encoder_options'])) {
|
||||||
$info['audio']['encoder_options'] = trim($info['audio']['encoder_options']);
|
$info['audio']['encoder_options'] = trim($info['audio']['encoder_options']);
|
||||||
} elseif (isset($info['audio']['encoder_options'])) {
|
} elseif (isset($info['audio']['encoder_options'])) {
|
||||||
unset($info['audio']['encoder_options']);
|
unset($info['audio']['encoder_options']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x26: // ID_MD5_CHECKSUM
|
case 0x26: // ID_MD5_CHECKSUM
|
||||||
if (strlen($metablock['data']) == 16) {
|
if (strlen($metablock['data']) == 16) {
|
||||||
$info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
|
$info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
|
||||||
} else {
|
} else {
|
||||||
$info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes';
|
$this->warning('Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x00: // ID_DUMMY
|
case 0x00: // ID_DUMMY
|
||||||
case 0x01: // ID_ENCODER_INFO
|
case 0x01: // ID_ENCODER_INFO
|
||||||
case 0x02: // ID_DECORR_TERMS
|
case 0x02: // ID_DECORR_TERMS
|
||||||
case 0x03: // ID_DECORR_WEIGHTS
|
case 0x03: // ID_DECORR_WEIGHTS
|
||||||
case 0x04: // ID_DECORR_SAMPLES
|
case 0x04: // ID_DECORR_SAMPLES
|
||||||
case 0x05: // ID_ENTROPY_VARS
|
case 0x05: // ID_ENTROPY_VARS
|
||||||
case 0x06: // ID_HYBRID_PROFILE
|
case 0x06: // ID_HYBRID_PROFILE
|
||||||
case 0x07: // ID_SHAPING_WEIGHTS
|
case 0x07: // ID_SHAPING_WEIGHTS
|
||||||
case 0x08: // ID_FLOAT_INFO
|
case 0x08: // ID_FLOAT_INFO
|
||||||
case 0x09: // ID_INT32_INFO
|
case 0x09: // ID_INT32_INFO
|
||||||
case 0x0A: // ID_WV_BITSTREAM
|
case 0x0A: // ID_WV_BITSTREAM
|
||||||
case 0x0B: // ID_WVC_BITSTREAM
|
case 0x0B: // ID_WVC_BITSTREAM
|
||||||
case 0x0C: // ID_WVX_BITSTREAM
|
case 0x0C: // ID_WVX_BITSTREAM
|
||||||
case 0x0D: // ID_CHANNEL_INFO
|
case 0x0D: // ID_CHANNEL_INFO
|
||||||
unset($metablock);
|
unset($metablock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!empty($metablock)) {
|
|
||||||
$info['wavpack']['metablocks'][] = $metablock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['audio']['encoder'] = 'WavPack v'.$info['wavpack']['blockheader']['major_version'].'.'.str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
|
}
|
||||||
$info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
|
if (!empty($metablock)) {
|
||||||
$info['audio']['channels'] = ($info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
|
$info['wavpack']['metablocks'][] = $metablock;
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($info['playtime_seconds'])) {
|
}
|
||||||
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
|
||||||
} else {
|
|
||||||
$info['audio']['dataformat'] = 'wvc';
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
$info['audio']['encoder'] = 'WavPack v'.$info['wavpack']['blockheader']['major_version'].'.'.str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
|
||||||
|
$info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
|
||||||
|
$info['audio']['channels'] = ($info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
|
||||||
|
|
||||||
|
if (!empty($info['playtime_seconds'])) {
|
||||||
|
|
||||||
|
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$info['audio']['dataformat'] = 'wvc';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function WavPackMetablockNameLookup(&$id)
|
public function WavPackMetablockNameLookup(&$id) {
|
||||||
{
|
static $WavPackMetablockNameLookup = array(
|
||||||
static $WavPackMetablockNameLookup = [
|
0x00 => 'Dummy',
|
||||||
0x00 => 'Dummy',
|
0x01 => 'Encoder Info',
|
||||||
0x01 => 'Encoder Info',
|
0x02 => 'Decorrelation Terms',
|
||||||
0x02 => 'Decorrelation Terms',
|
0x03 => 'Decorrelation Weights',
|
||||||
0x03 => 'Decorrelation Weights',
|
0x04 => 'Decorrelation Samples',
|
||||||
0x04 => 'Decorrelation Samples',
|
0x05 => 'Entropy Variables',
|
||||||
0x05 => 'Entropy Variables',
|
0x06 => 'Hybrid Profile',
|
||||||
0x06 => 'Hybrid Profile',
|
0x07 => 'Shaping Weights',
|
||||||
0x07 => 'Shaping Weights',
|
0x08 => 'Float Info',
|
||||||
0x08 => 'Float Info',
|
0x09 => 'Int32 Info',
|
||||||
0x09 => 'Int32 Info',
|
0x0A => 'WV Bitstream',
|
||||||
0x0A => 'WV Bitstream',
|
0x0B => 'WVC Bitstream',
|
||||||
0x0B => 'WVC Bitstream',
|
0x0C => 'WVX Bitstream',
|
||||||
0x0C => 'WVX Bitstream',
|
0x0D => 'Channel Info',
|
||||||
0x0D => 'Channel Info',
|
0x21 => 'RIFF header',
|
||||||
0x21 => 'RIFF header',
|
0x22 => 'RIFF trailer',
|
||||||
0x22 => 'RIFF trailer',
|
0x23 => 'Replay Gain',
|
||||||
0x23 => 'Replay Gain',
|
0x24 => 'Cuesheet',
|
||||||
0x24 => 'Cuesheet',
|
0x25 => 'Config Block',
|
||||||
0x25 => 'Config Block',
|
0x26 => 'MD5 Checksum',
|
||||||
0x26 => 'MD5 Checksum',
|
);
|
||||||
];
|
return (isset($WavPackMetablockNameLookup[$id]) ? $WavPackMetablockNameLookup[$id] : '');
|
||||||
return (isset($WavPackMetablockNameLookup[$id]) ? $WavPackMetablockNameLookup[$id] : '');
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,34 +18,34 @@
|
||||||
class getid3_efax extends getid3_handler
|
class getid3_efax extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$efaxheader = $this->fread(1024);
|
$efaxheader = $this->fread(1024);
|
||||||
|
|
||||||
$info['efax']['header']['magic'] = substr($efaxheader, 0, 2);
|
$info['efax']['header']['magic'] = substr($efaxheader, 0, 2);
|
||||||
if ($info['efax']['header']['magic'] != "\xDC\xFE") {
|
if ($info['efax']['header']['magic'] != "\xDC\xFE") {
|
||||||
$info['error'][] = 'Invalid eFax byte order identifier (expecting DC FE, found '.getid3_lib::PrintHexBytes($info['efax']['header']['magic']).') at offset '.$info['avdataoffset'];
|
$this->error('Invalid eFax byte order identifier (expecting DC FE, found '.getid3_lib::PrintHexBytes($info['efax']['header']['magic']).') at offset '.$info['avdataoffset']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'efax';
|
$info['fileformat'] = 'efax';
|
||||||
|
|
||||||
$info['efax']['header']['filesize'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 2, 4));
|
$info['efax']['header']['filesize'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 2, 4));
|
||||||
if ($info['efax']['header']['filesize'] != $info['filesize']) {
|
if ($info['efax']['header']['filesize'] != $info['filesize']) {
|
||||||
$info['error'][] = 'Probable '.(($info['efax']['header']['filesize'] > $info['filesize']) ? 'truncated' : 'corrupt').' file, expecting '.$info['efax']['header']['filesize'].' bytes, found '.$info['filesize'].' bytes';
|
$this->error('Probable '.(($info['efax']['header']['filesize'] > $info['filesize']) ? 'truncated' : 'corrupt').' file, expecting '.$info['efax']['header']['filesize'].' bytes, found '.$info['filesize'].' bytes');
|
||||||
}
|
}
|
||||||
$info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), "\x00");
|
$info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), "\x00");
|
||||||
$info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), "\x00");
|
$info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), "\x00");
|
||||||
$info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), "\x00");
|
$info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), "\x00");
|
||||||
|
|
||||||
$info['efax']['header']['pages'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 198, 2));
|
$info['efax']['header']['pages'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 198, 2));
|
||||||
$info['efax']['header']['data_bytes'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 202, 4));
|
$info['efax']['header']['data_bytes'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 202, 4));
|
||||||
|
|
||||||
$info['error'][] = 'eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
$this->error('eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,66 +18,65 @@
|
||||||
class getid3_gif extends getid3_handler
|
class getid3_gif extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'gif';
|
$info['fileformat'] = 'gif';
|
||||||
$info['video']['dataformat'] = 'gif';
|
$info['video']['dataformat'] = 'gif';
|
||||||
$info['video']['lossless'] = true;
|
$info['video']['lossless'] = true;
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$GIFheader = $this->fread(13);
|
$GIFheader = $this->fread(13);
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
|
||||||
$info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
|
$info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
|
||||||
$offset += 3;
|
$offset += 3;
|
||||||
|
|
||||||
$magic = 'GIF';
|
$magic = 'GIF';
|
||||||
if ($info['gif']['header']['raw']['identifier'] != $magic) {
|
if ($info['gif']['header']['raw']['identifier'] != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['gif']['header']['raw']['identifier']).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['gif']['header']['raw']['identifier']).'"');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['gif']);
|
unset($info['gif']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
|
$info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
|
||||||
$offset += 3;
|
$offset += 3;
|
||||||
$info['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
$info['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$info['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
$info['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$info['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
$info['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$info['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
$info['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$info['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
$info['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
|
|
||||||
$info['video']['resolution_x'] = $info['gif']['header']['raw']['width'];
|
$info['video']['resolution_x'] = $info['gif']['header']['raw']['width'];
|
||||||
$info['video']['resolution_y'] = $info['gif']['header']['raw']['height'];
|
$info['video']['resolution_y'] = $info['gif']['header']['raw']['height'];
|
||||||
$info['gif']['version'] = $info['gif']['header']['raw']['version'];
|
$info['gif']['version'] = $info['gif']['header']['raw']['version'];
|
||||||
$info['gif']['header']['flags']['global_color_table'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x80);
|
$info['gif']['header']['flags']['global_color_table'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x80);
|
||||||
if ($info['gif']['header']['raw']['flags'] & 0x80) {
|
if ($info['gif']['header']['raw']['flags'] & 0x80) {
|
||||||
// Number of bits per primary color available to the original image, minus 1
|
// Number of bits per primary color available to the original image, minus 1
|
||||||
$info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
|
$info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
|
||||||
} else {
|
} else {
|
||||||
$info['gif']['header']['bits_per_pixel'] = 0;
|
$info['gif']['header']['bits_per_pixel'] = 0;
|
||||||
}
|
}
|
||||||
$info['gif']['header']['flags']['global_color_sorted'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x40);
|
$info['gif']['header']['flags']['global_color_sorted'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x40);
|
||||||
if ($info['gif']['header']['flags']['global_color_table']) {
|
if ($info['gif']['header']['flags']['global_color_table']) {
|
||||||
// the number of bytes contained in the Global Color Table. To determine that
|
// 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]
|
// actual size of the color table, raise 2 to [the value of the field + 1]
|
||||||
$info['gif']['header']['global_color_size'] = pow(2, ($info['gif']['header']['raw']['flags'] & 0x07) + 1);
|
$info['gif']['header']['global_color_size'] = pow(2, ($info['gif']['header']['raw']['flags'] & 0x07) + 1);
|
||||||
$info['video']['bits_per_sample'] = ($info['gif']['header']['raw']['flags'] & 0x07) + 1;
|
$info['video']['bits_per_sample'] = ($info['gif']['header']['raw']['flags'] & 0x07) + 1;
|
||||||
} else {
|
} else {
|
||||||
$info['gif']['header']['global_color_size'] = 0;
|
$info['gif']['header']['global_color_size'] = 0;
|
||||||
}
|
}
|
||||||
if ($info['gif']['header']['raw']['aspect_ratio'] != 0) {
|
if ($info['gif']['header']['raw']['aspect_ratio'] != 0) {
|
||||||
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
|
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
|
||||||
$info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
|
$info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if ($info['gif']['header']['flags']['global_color_table']) {
|
// if ($info['gif']['header']['flags']['global_color_table']) {
|
||||||
// $GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
|
// $GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
|
||||||
|
@ -110,7 +109,7 @@ class getid3_gif extends getid3_handler
|
||||||
//
|
//
|
||||||
// if ($ImageDescriptor['flags']['use_local_color_map']) {
|
// if ($ImageDescriptor['flags']['use_local_color_map']) {
|
||||||
//
|
//
|
||||||
// $info['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
|
// $this->warning('This version of getID3() cannot parse local color maps for GIFs');
|
||||||
// return true;
|
// return true;
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
|
@ -165,19 +164,19 @@ class getid3_gif extends getid3_handler
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function GetLSBits($bits)
|
public function GetLSBits($bits) {
|
||||||
{
|
static $bitbuffer = '';
|
||||||
static $bitbuffer = '';
|
while (strlen($bitbuffer) < $bits) {
|
||||||
while (strlen($bitbuffer) < $bits) {
|
$bitbuffer = str_pad(decbin(ord($this->fread(1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
|
||||||
$bitbuffer = str_pad(decbin(ord($this->fread(1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
|
}
|
||||||
}
|
$value = bindec(substr($bitbuffer, 0 - $bits));
|
||||||
$value = bindec(substr($bitbuffer, 0 - $bits));
|
$bitbuffer = substr($bitbuffer, 0, 0 - $bits);
|
||||||
$bitbuffer = substr($bitbuffer, 0, 0 - $bits);
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,319 +20,330 @@ class getid3_jpg extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'jpg';
|
$info['fileformat'] = 'jpg';
|
||||||
$info['video']['dataformat'] = 'jpg';
|
$info['video']['dataformat'] = 'jpg';
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
$info['video']['bits_per_sample'] = 24;
|
$info['video']['bits_per_sample'] = 24;
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
$imageinfo = [];
|
$imageinfo = array();
|
||||||
//list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo);
|
//list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo);
|
||||||
list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474
|
list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474
|
||||||
|
|
||||||
|
|
||||||
if (isset($imageinfo['APP13'])) {
|
if (isset($imageinfo['APP13'])) {
|
||||||
// http://php.net/iptcparse
|
// http://php.net/iptcparse
|
||||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||||
$iptc_parsed = iptcparse($imageinfo['APP13']);
|
$iptc_parsed = iptcparse($imageinfo['APP13']);
|
||||||
if (is_array($iptc_parsed)) {
|
if (is_array($iptc_parsed)) {
|
||||||
foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
|
foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
|
||||||
list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
|
list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
|
||||||
$iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
|
$iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
|
||||||
foreach ($iptc_values as $key => $value) {
|
foreach ($iptc_values as $key => $value) {
|
||||||
$IPTCrecordName = $this->IPTCrecordName($iptc_record);
|
$IPTCrecordName = $this->IPTCrecordName($iptc_record);
|
||||||
$IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey);
|
$IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey);
|
||||||
if (isset($info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName])) {
|
if (isset($info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName])) {
|
||||||
$info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value;
|
$info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value;
|
||||||
} else {
|
} else {
|
||||||
$info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName] = [$value];
|
$info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName] = array($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$returnOK = false;
|
$returnOK = false;
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case IMG_JPG:
|
case IMG_JPG:
|
||||||
$info['video']['resolution_x'] = $width;
|
$info['video']['resolution_x'] = $width;
|
||||||
$info['video']['resolution_y'] = $height;
|
$info['video']['resolution_y'] = $height;
|
||||||
|
|
||||||
if (isset($imageinfo['APP1'])) {
|
if (isset($imageinfo['APP1'])) {
|
||||||
if (function_exists('exif_read_data')) {
|
if (function_exists('exif_read_data')) {
|
||||||
if (substr($imageinfo['APP1'], 0, 4) == 'Exif') {
|
if (substr($imageinfo['APP1'], 0, 4) == 'Exif') {
|
||||||
//$info['warning'][] = 'known issue: https://bugs.php.net/bug.php?id=62523';
|
//$this->warning('known issue: https://bugs.php.net/bug.php?id=62523');
|
||||||
//return false;
|
//return false;
|
||||||
$info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false);
|
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
|
||||||
} else {
|
if (!(error_reporting() & $errno)) {
|
||||||
$info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")';
|
// error is not specified in the error_reporting setting, so we ignore it
|
||||||
}
|
return false;
|
||||||
} else {
|
}
|
||||||
$info['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$returnOK = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
$errcontext['info']['warning'][] = 'Error parsing EXIF data ('.$errstr.')';
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
|
$info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false);
|
||||||
|
|
||||||
|
restore_error_handler();
|
||||||
|
} else {
|
||||||
|
$this->warning('exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->warning('EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$returnOK = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$cast_as_appropriate_keys = ['EXIF', 'IFD0', 'THUMBNAIL'];
|
$cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL');
|
||||||
foreach ($cast_as_appropriate_keys as $exif_key) {
|
foreach ($cast_as_appropriate_keys as $exif_key) {
|
||||||
if (isset($info['jpg']['exif'][$exif_key])) {
|
if (isset($info['jpg']['exif'][$exif_key])) {
|
||||||
foreach ($info['jpg']['exif'][$exif_key] as $key => $value) {
|
foreach ($info['jpg']['exif'][$exif_key] as $key => $value) {
|
||||||
$info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value);
|
$info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isset($info['jpg']['exif']['GPS'])) {
|
if (isset($info['jpg']['exif']['GPS'])) {
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) {
|
|
||||||
for ($i = 0; $i < 4; $i++) {
|
|
||||||
$version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1));
|
|
||||||
}
|
|
||||||
$info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) {
|
if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) {
|
||||||
$explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']);
|
for ($i = 0; $i < 4; $i++) {
|
||||||
$computed_time[5] = (isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : '');
|
$version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1));
|
||||||
$computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : '');
|
}
|
||||||
$computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : '');
|
$info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts);
|
||||||
|
}
|
||||||
|
|
||||||
$computed_time = [0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0];
|
if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) {
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) {
|
$explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']);
|
||||||
foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) {
|
$computed_time[5] = (isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : '');
|
||||||
$computed_time[$key] = getid3_lib::DecimalizeFraction($value);
|
$computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : '');
|
||||||
}
|
$computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : '');
|
||||||
}
|
|
||||||
$info['jpg']['exif']['GPS']['computed']['timestamp'] = gmmktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) {
|
$computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0);
|
||||||
$direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1);
|
if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) {
|
||||||
foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) {
|
foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) {
|
||||||
$computed_latitude[$key] = getid3_lib::DecimalizeFraction($value);
|
$computed_time[$key] = getid3_lib::DecimalizeFraction($value);
|
||||||
}
|
}
|
||||||
$info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600));
|
}
|
||||||
}
|
$info['jpg']['exif']['GPS']['computed']['timestamp'] = gmmktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) {
|
if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) {
|
||||||
$direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1);
|
$direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1);
|
||||||
foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) {
|
foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) {
|
||||||
$computed_longitude[$key] = getid3_lib::DecimalizeFraction($value);
|
$computed_latitude[$key] = getid3_lib::DecimalizeFraction($value);
|
||||||
}
|
}
|
||||||
$info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
|
$info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600));
|
||||||
}
|
}
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSAltitudeRef'])) {
|
|
||||||
$info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level
|
if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) {
|
||||||
}
|
$direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1);
|
||||||
if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) {
|
foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) {
|
||||||
$direction_multiplier = (!empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1); // 0 = above sea level; 1 = below sea level
|
$computed_longitude[$key] = getid3_lib::DecimalizeFraction($value);
|
||||||
$info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']);
|
}
|
||||||
}
|
$info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
|
||||||
}
|
}
|
||||||
|
if (isset($info['jpg']['exif']['GPS']['GPSAltitudeRef'])) {
|
||||||
|
$info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level
|
||||||
|
}
|
||||||
|
if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) {
|
||||||
|
$direction_multiplier = (!empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1); // 0 = above sea level; 1 = below sea level
|
||||||
|
$info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, true);
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, true);
|
||||||
if (isset($info['filenamepath'])) {
|
if (isset($info['filenamepath'])) {
|
||||||
$image_xmp = new Image_XMP($info['filenamepath']);
|
$image_xmp = new Image_XMP($info['filenamepath']);
|
||||||
$xmp_raw = $image_xmp->getAllTags();
|
$xmp_raw = $image_xmp->getAllTags();
|
||||||
foreach ($xmp_raw as $key => $value) {
|
foreach ($xmp_raw as $key => $value) {
|
||||||
if (strpos($key, ':')) {
|
if (strpos($key, ':')) {
|
||||||
list($subsection, $tagname) = explode(':', $key);
|
list($subsection, $tagname) = explode(':', $key);
|
||||||
$info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
|
$info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
|
||||||
} else {
|
} else {
|
||||||
$info['warning'][] = 'XMP: expecting "<subsection>:<tagname>", found "'.$key.'"';
|
$this->warning('XMP: expecting "<subsection>:<tagname>", found "'.$key.'"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$returnOK) {
|
if (!$returnOK) {
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function CastAsAppropriate($value)
|
public function CastAsAppropriate($value) {
|
||||||
{
|
if (is_array($value)) {
|
||||||
if (is_array($value)) {
|
return $value;
|
||||||
return $value;
|
} elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
|
||||||
} elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
|
return getid3_lib::DecimalizeFraction($value);
|
||||||
return getid3_lib::DecimalizeFraction($value);
|
} elseif (preg_match('#^[0-9]+$#', $value)) {
|
||||||
} elseif (preg_match('#^[0-9]+$#', $value)) {
|
return getid3_lib::CastAsInt($value);
|
||||||
return getid3_lib::CastAsInt($value);
|
} elseif (preg_match('#^[0-9\.]+$#', $value)) {
|
||||||
} elseif (preg_match('#^[0-9\.]+$#', $value)) {
|
return (float) $value;
|
||||||
return (float) $value;
|
}
|
||||||
}
|
return $value;
|
||||||
return $value;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function IPTCrecordName($iptc_record)
|
public function IPTCrecordName($iptc_record) {
|
||||||
{
|
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
static $IPTCrecordName = array();
|
||||||
static $IPTCrecordName = [];
|
if (empty($IPTCrecordName)) {
|
||||||
if (empty($IPTCrecordName)) {
|
$IPTCrecordName = array(
|
||||||
$IPTCrecordName = [
|
1 => 'IPTCEnvelope',
|
||||||
1 => 'IPTCEnvelope',
|
2 => 'IPTCApplication',
|
||||||
2 => 'IPTCApplication',
|
3 => 'IPTCNewsPhoto',
|
||||||
3 => 'IPTCNewsPhoto',
|
7 => 'IPTCPreObjectData',
|
||||||
7 => 'IPTCPreObjectData',
|
8 => 'IPTCObjectData',
|
||||||
8 => 'IPTCObjectData',
|
9 => 'IPTCPostObjectData',
|
||||||
9 => 'IPTCPostObjectData',
|
);
|
||||||
];
|
}
|
||||||
}
|
return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
|
||||||
return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function IPTCrecordTagName($iptc_record, $iptc_tagkey)
|
public function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
|
||||||
{
|
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
||||||
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
|
static $IPTCrecordTagName = array();
|
||||||
static $IPTCrecordTagName = [];
|
if (empty($IPTCrecordTagName)) {
|
||||||
if (empty($IPTCrecordTagName)) {
|
$IPTCrecordTagName = array(
|
||||||
$IPTCrecordTagName = [
|
1 => array( // IPTC EnvelopeRecord Tags
|
||||||
1 => [ // IPTC EnvelopeRecord Tags
|
0 => 'EnvelopeRecordVersion',
|
||||||
0 => 'EnvelopeRecordVersion',
|
5 => 'Destination',
|
||||||
5 => 'Destination',
|
20 => 'FileFormat',
|
||||||
20 => 'FileFormat',
|
22 => 'FileVersion',
|
||||||
22 => 'FileVersion',
|
30 => 'ServiceIdentifier',
|
||||||
30 => 'ServiceIdentifier',
|
40 => 'EnvelopeNumber',
|
||||||
40 => 'EnvelopeNumber',
|
50 => 'ProductID',
|
||||||
50 => 'ProductID',
|
60 => 'EnvelopePriority',
|
||||||
60 => 'EnvelopePriority',
|
70 => 'DateSent',
|
||||||
70 => 'DateSent',
|
80 => 'TimeSent',
|
||||||
80 => 'TimeSent',
|
90 => 'CodedCharacterSet',
|
||||||
90 => 'CodedCharacterSet',
|
100 => 'UniqueObjectName',
|
||||||
100 => 'UniqueObjectName',
|
120 => 'ARMIdentifier',
|
||||||
120 => 'ARMIdentifier',
|
122 => 'ARMVersion',
|
||||||
122 => 'ARMVersion',
|
),
|
||||||
],
|
2 => array( // IPTC ApplicationRecord Tags
|
||||||
2 => [ // IPTC ApplicationRecord Tags
|
0 => 'ApplicationRecordVersion',
|
||||||
0 => 'ApplicationRecordVersion',
|
3 => 'ObjectTypeReference',
|
||||||
3 => 'ObjectTypeReference',
|
4 => 'ObjectAttributeReference',
|
||||||
4 => 'ObjectAttributeReference',
|
5 => 'ObjectName',
|
||||||
5 => 'ObjectName',
|
7 => 'EditStatus',
|
||||||
7 => 'EditStatus',
|
8 => 'EditorialUpdate',
|
||||||
8 => 'EditorialUpdate',
|
10 => 'Urgency',
|
||||||
10 => 'Urgency',
|
12 => 'SubjectReference',
|
||||||
12 => 'SubjectReference',
|
15 => 'Category',
|
||||||
15 => 'Category',
|
20 => 'SupplementalCategories',
|
||||||
20 => 'SupplementalCategories',
|
22 => 'FixtureIdentifier',
|
||||||
22 => 'FixtureIdentifier',
|
25 => 'Keywords',
|
||||||
25 => 'Keywords',
|
26 => 'ContentLocationCode',
|
||||||
26 => 'ContentLocationCode',
|
27 => 'ContentLocationName',
|
||||||
27 => 'ContentLocationName',
|
30 => 'ReleaseDate',
|
||||||
30 => 'ReleaseDate',
|
35 => 'ReleaseTime',
|
||||||
35 => 'ReleaseTime',
|
37 => 'ExpirationDate',
|
||||||
37 => 'ExpirationDate',
|
38 => 'ExpirationTime',
|
||||||
38 => 'ExpirationTime',
|
40 => 'SpecialInstructions',
|
||||||
40 => 'SpecialInstructions',
|
42 => 'ActionAdvised',
|
||||||
42 => 'ActionAdvised',
|
45 => 'ReferenceService',
|
||||||
45 => 'ReferenceService',
|
47 => 'ReferenceDate',
|
||||||
47 => 'ReferenceDate',
|
50 => 'ReferenceNumber',
|
||||||
50 => 'ReferenceNumber',
|
55 => 'DateCreated',
|
||||||
55 => 'DateCreated',
|
60 => 'TimeCreated',
|
||||||
60 => 'TimeCreated',
|
62 => 'DigitalCreationDate',
|
||||||
62 => 'DigitalCreationDate',
|
63 => 'DigitalCreationTime',
|
||||||
63 => 'DigitalCreationTime',
|
65 => 'OriginatingProgram',
|
||||||
65 => 'OriginatingProgram',
|
70 => 'ProgramVersion',
|
||||||
70 => 'ProgramVersion',
|
75 => 'ObjectCycle',
|
||||||
75 => 'ObjectCycle',
|
80 => 'By-line',
|
||||||
80 => 'By-line',
|
85 => 'By-lineTitle',
|
||||||
85 => 'By-lineTitle',
|
90 => 'City',
|
||||||
90 => 'City',
|
92 => 'Sub-location',
|
||||||
92 => 'Sub-location',
|
95 => 'Province-State',
|
||||||
95 => 'Province-State',
|
100 => 'Country-PrimaryLocationCode',
|
||||||
100 => 'Country-PrimaryLocationCode',
|
101 => 'Country-PrimaryLocationName',
|
||||||
101 => 'Country-PrimaryLocationName',
|
103 => 'OriginalTransmissionReference',
|
||||||
103 => 'OriginalTransmissionReference',
|
105 => 'Headline',
|
||||||
105 => 'Headline',
|
110 => 'Credit',
|
||||||
110 => 'Credit',
|
115 => 'Source',
|
||||||
115 => 'Source',
|
116 => 'CopyrightNotice',
|
||||||
116 => 'CopyrightNotice',
|
118 => 'Contact',
|
||||||
118 => 'Contact',
|
120 => 'Caption-Abstract',
|
||||||
120 => 'Caption-Abstract',
|
121 => 'LocalCaption',
|
||||||
121 => 'LocalCaption',
|
122 => 'Writer-Editor',
|
||||||
122 => 'Writer-Editor',
|
125 => 'RasterizedCaption',
|
||||||
125 => 'RasterizedCaption',
|
130 => 'ImageType',
|
||||||
130 => 'ImageType',
|
131 => 'ImageOrientation',
|
||||||
131 => 'ImageOrientation',
|
135 => 'LanguageIdentifier',
|
||||||
135 => 'LanguageIdentifier',
|
150 => 'AudioType',
|
||||||
150 => 'AudioType',
|
151 => 'AudioSamplingRate',
|
||||||
151 => 'AudioSamplingRate',
|
152 => 'AudioSamplingResolution',
|
||||||
152 => 'AudioSamplingResolution',
|
153 => 'AudioDuration',
|
||||||
153 => 'AudioDuration',
|
154 => 'AudioOutcue',
|
||||||
154 => 'AudioOutcue',
|
184 => 'JobID',
|
||||||
184 => 'JobID',
|
185 => 'MasterDocumentID',
|
||||||
185 => 'MasterDocumentID',
|
186 => 'ShortDocumentID',
|
||||||
186 => 'ShortDocumentID',
|
187 => 'UniqueDocumentID',
|
||||||
187 => 'UniqueDocumentID',
|
188 => 'OwnerID',
|
||||||
188 => 'OwnerID',
|
200 => 'ObjectPreviewFileFormat',
|
||||||
200 => 'ObjectPreviewFileFormat',
|
201 => 'ObjectPreviewFileVersion',
|
||||||
201 => 'ObjectPreviewFileVersion',
|
202 => 'ObjectPreviewData',
|
||||||
202 => 'ObjectPreviewData',
|
221 => 'Prefs',
|
||||||
221 => 'Prefs',
|
225 => 'ClassifyState',
|
||||||
225 => 'ClassifyState',
|
228 => 'SimilarityIndex',
|
||||||
228 => 'SimilarityIndex',
|
230 => 'DocumentNotes',
|
||||||
230 => 'DocumentNotes',
|
231 => 'DocumentHistory',
|
||||||
231 => 'DocumentHistory',
|
232 => 'ExifCameraInfo',
|
||||||
232 => 'ExifCameraInfo',
|
),
|
||||||
],
|
3 => array( // IPTC NewsPhoto Tags
|
||||||
3 => [ // IPTC NewsPhoto Tags
|
0 => 'NewsPhotoVersion',
|
||||||
0 => 'NewsPhotoVersion',
|
10 => 'IPTCPictureNumber',
|
||||||
10 => 'IPTCPictureNumber',
|
20 => 'IPTCImageWidth',
|
||||||
20 => 'IPTCImageWidth',
|
30 => 'IPTCImageHeight',
|
||||||
30 => 'IPTCImageHeight',
|
40 => 'IPTCPixelWidth',
|
||||||
40 => 'IPTCPixelWidth',
|
50 => 'IPTCPixelHeight',
|
||||||
50 => 'IPTCPixelHeight',
|
55 => 'SupplementalType',
|
||||||
55 => 'SupplementalType',
|
60 => 'ColorRepresentation',
|
||||||
60 => 'ColorRepresentation',
|
64 => 'InterchangeColorSpace',
|
||||||
64 => 'InterchangeColorSpace',
|
65 => 'ColorSequence',
|
||||||
65 => 'ColorSequence',
|
66 => 'ICC_Profile',
|
||||||
66 => 'ICC_Profile',
|
70 => 'ColorCalibrationMatrix',
|
||||||
70 => 'ColorCalibrationMatrix',
|
80 => 'LookupTable',
|
||||||
80 => 'LookupTable',
|
84 => 'NumIndexEntries',
|
||||||
84 => 'NumIndexEntries',
|
85 => 'ColorPalette',
|
||||||
85 => 'ColorPalette',
|
86 => 'IPTCBitsPerSample',
|
||||||
86 => 'IPTCBitsPerSample',
|
90 => 'SampleStructure',
|
||||||
90 => 'SampleStructure',
|
100 => 'ScanningDirection',
|
||||||
100 => 'ScanningDirection',
|
102 => 'IPTCImageRotation',
|
||||||
102 => 'IPTCImageRotation',
|
110 => 'DataCompressionMethod',
|
||||||
110 => 'DataCompressionMethod',
|
120 => 'QuantizationMethod',
|
||||||
120 => 'QuantizationMethod',
|
125 => 'EndPoints',
|
||||||
125 => 'EndPoints',
|
130 => 'ExcursionTolerance',
|
||||||
130 => 'ExcursionTolerance',
|
135 => 'BitsPerComponent',
|
||||||
135 => 'BitsPerComponent',
|
140 => 'MaximumDensityRange',
|
||||||
140 => 'MaximumDensityRange',
|
145 => 'GammaCompensatedValue',
|
||||||
145 => 'GammaCompensatedValue',
|
),
|
||||||
],
|
7 => array( // IPTC PreObjectData Tags
|
||||||
7 => [ // IPTC PreObjectData Tags
|
10 => 'SizeMode',
|
||||||
10 => 'SizeMode',
|
20 => 'MaxSubfileSize',
|
||||||
20 => 'MaxSubfileSize',
|
90 => 'ObjectSizeAnnounced',
|
||||||
90 => 'ObjectSizeAnnounced',
|
95 => 'MaximumObjectSize',
|
||||||
95 => 'MaximumObjectSize',
|
),
|
||||||
],
|
8 => array( // IPTC ObjectData Tags
|
||||||
8 => [ // IPTC ObjectData Tags
|
10 => 'SubFile',
|
||||||
10 => 'SubFile',
|
),
|
||||||
],
|
9 => array( // IPTC PostObjectData Tags
|
||||||
9 => [ // IPTC PostObjectData Tags
|
10 => 'ConfirmedObjectSize',
|
||||||
10 => 'ConfirmedObjectSize',
|
),
|
||||||
],
|
);
|
||||||
];
|
|
||||||
}
|
}
|
||||||
return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
|
return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,115 +17,117 @@
|
||||||
|
|
||||||
class getid3_pcd extends getid3_handler
|
class getid3_pcd extends getid3_handler
|
||||||
{
|
{
|
||||||
public $ExtractData = 0;
|
public $ExtractData = 0;
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'pcd';
|
$info['fileformat'] = 'pcd';
|
||||||
$info['video']['dataformat'] = 'pcd';
|
$info['video']['dataformat'] = 'pcd';
|
||||||
$info['video']['lossless'] = false;
|
$info['video']['lossless'] = false;
|
||||||
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset'] + 72);
|
$this->fseek($info['avdataoffset'] + 72);
|
||||||
|
|
||||||
$PCDflags = $this->fread(1);
|
$PCDflags = $this->fread(1);
|
||||||
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
|
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
|
||||||
|
|
||||||
|
|
||||||
if ($PCDisVertical) {
|
if ($PCDisVertical) {
|
||||||
$info['video']['resolution_x'] = 3072;
|
$info['video']['resolution_x'] = 3072;
|
||||||
$info['video']['resolution_y'] = 2048;
|
$info['video']['resolution_y'] = 2048;
|
||||||
} else {
|
} else {
|
||||||
$info['video']['resolution_x'] = 2048;
|
$info['video']['resolution_x'] = 2048;
|
||||||
$info['video']['resolution_y'] = 3072;
|
$info['video']['resolution_y'] = 3072;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($this->ExtractData > 3) {
|
if ($this->ExtractData > 3) {
|
||||||
$info['error'][] = 'Cannot extract PSD image data for detail levels above BASE (level-3) because encrypted with Kodak-proprietary compression/encryption.';
|
|
||||||
} elseif ($this->ExtractData > 0) {
|
|
||||||
$PCD_levels[1] = [ 192, 128, 0x02000]; // BASE/16
|
|
||||||
$PCD_levels[2] = [ 384, 256, 0x0B800]; // BASE/4
|
|
||||||
$PCD_levels[3] = [ 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];
|
$this->error('Cannot extract PSD image data for detail levels above BASE (level-3) because encrypted with Kodak-proprietary compression/encryption.');
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset'] + $PCD_dataOffset);
|
} elseif ($this->ExtractData > 0) {
|
||||||
|
|
||||||
for ($y = 0; $y < $PCD_height; $y += 2) {
|
$PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16
|
||||||
// The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h.
|
$PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4
|
||||||
// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
|
$PCD_levels[3] = array( 768, 512, 0x30000); // BASE
|
||||||
// consisting of w bytes, where w is the width of the image-subtype. The first w bytes and
|
//$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption
|
||||||
// the first half of the third w bytes contain data for the first RGB-line, the second w bytes
|
//$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption
|
||||||
// and the second half of the third w bytes contain data for a second RGB-line.
|
//$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only
|
||||||
|
|
||||||
$PCD_data_Y1 = $this->fread($PCD_width);
|
list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3];
|
||||||
$PCD_data_Y2 = $this->fread($PCD_width);
|
|
||||||
$PCD_data_Cb = $this->fread(intval(round($PCD_width / 2)));
|
|
||||||
$PCD_data_Cr = $this->fread(intval(round($PCD_width / 2)));
|
|
||||||
|
|
||||||
for ($x = 0; $x < $PCD_width; $x++) {
|
$this->fseek($info['avdataoffset'] + $PCD_dataOffset);
|
||||||
if ($PCDisVertical) {
|
|
||||||
$info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
|
||||||
$info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
|
||||||
} else {
|
|
||||||
$info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
|
||||||
$info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example for plotting extracted data
|
for ($y = 0; $y < $PCD_height; $y += 2) {
|
||||||
//getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
|
// The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h.
|
||||||
//if ($PCDisVertical) {
|
// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
|
||||||
// $BMPinfo['resolution_x'] = $PCD_height;
|
// consisting of w bytes, where w is the width of the image-subtype. The first w bytes and
|
||||||
// $BMPinfo['resolution_y'] = $PCD_width;
|
// the first half of the third w bytes contain data for the first RGB-line, the second w bytes
|
||||||
//} else {
|
// and the second half of the third w bytes contain data for a second RGB-line.
|
||||||
// $BMPinfo['resolution_x'] = $PCD_width;
|
|
||||||
// $BMPinfo['resolution_y'] = $PCD_height;
|
|
||||||
//}
|
|
||||||
//$BMPinfo['bmp']['data'] = $info['pcd']['data'];
|
|
||||||
//getid3_bmp::PlotBMP($BMPinfo);
|
|
||||||
//exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function YCbCr2RGB($Y, $Cb, $Cr)
|
$PCD_data_Y1 = $this->fread($PCD_width);
|
||||||
{
|
$PCD_data_Y2 = $this->fread($PCD_width);
|
||||||
static $YCbCr_constants = [];
|
$PCD_data_Cb = $this->fread(intval(round($PCD_width / 2)));
|
||||||
if (empty($YCbCr_constants)) {
|
$PCD_data_Cr = $this->fread(intval(round($PCD_width / 2)));
|
||||||
$YCbCr_constants['red']['Y'] = 0.0054980 * 256;
|
|
||||||
$YCbCr_constants['red']['Cb'] = 0.0000000 * 256;
|
for ($x = 0; $x < $PCD_width; $x++) {
|
||||||
$YCbCr_constants['red']['Cr'] = 0.0051681 * 256;
|
if ($PCDisVertical) {
|
||||||
$YCbCr_constants['green']['Y'] = 0.0054980 * 256;
|
$info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||||
$YCbCr_constants['green']['Cb'] = -0.0015446 * 256;
|
$info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||||
$YCbCr_constants['green']['Cr'] = -0.0026325 * 256;
|
} else {
|
||||||
$YCbCr_constants['blue']['Y'] = 0.0054980 * 256;
|
$info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||||
$YCbCr_constants['blue']['Cb'] = 0.0079533 * 256;
|
$info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
|
||||||
$YCbCr_constants['blue']['Cr'] = 0.0000000 * 256;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] = $info['pcd']['data'];
|
||||||
|
//getid3_bmp::PlotBMP($BMPinfo);
|
||||||
|
//exit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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']);
|
||||||
|
}
|
||||||
|
|
||||||
$RGBcolor = ['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
|
@ -19,83 +19,84 @@ class getid3_svg extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
|
|
||||||
$SVGheader = $this->fread(4096);
|
$SVGheader = $this->fread(4096);
|
||||||
if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) {
|
if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) {
|
||||||
$info['svg']['xml']['raw'] = $matches;
|
$info['svg']['xml']['raw'] = $matches;
|
||||||
}
|
}
|
||||||
if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) {
|
if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) {
|
||||||
$info['svg']['doctype']['raw'] = $matches;
|
$info['svg']['doctype']['raw'] = $matches;
|
||||||
}
|
}
|
||||||
if (preg_match('#\<svg([^\>]+)\>#i', $SVGheader, $matches)) {
|
if (preg_match('#\<svg([^\>]+)\>#i', $SVGheader, $matches)) {
|
||||||
$info['svg']['svg']['raw'] = $matches;
|
$info['svg']['svg']['raw'] = $matches;
|
||||||
}
|
}
|
||||||
if (isset($info['svg']['svg']['raw'])) {
|
if (isset($info['svg']['svg']['raw'])) {
|
||||||
$sections_to_fix = ['xml', 'doctype', 'svg'];
|
|
||||||
foreach ($sections_to_fix as $section_to_fix) {
|
|
||||||
if (!isset($info['svg'][$section_to_fix])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$section_data = [];
|
|
||||||
while (preg_match('/ "([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
|
||||||
$section_data[] = $matches[1];
|
|
||||||
$info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]);
|
|
||||||
}
|
|
||||||
while (preg_match('/([^\s]+)="([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
|
||||||
$section_data[] = $matches[0];
|
|
||||||
$info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]);
|
|
||||||
}
|
|
||||||
$section_data = array_merge($section_data, preg_split('/[\s,]+/', $info['svg'][$section_to_fix]['raw'][1]));
|
|
||||||
foreach ($section_data as $keyvaluepair) {
|
|
||||||
$keyvaluepair = trim($keyvaluepair);
|
|
||||||
if ($keyvaluepair) {
|
|
||||||
$keyvalueexploded = explode('=', $keyvaluepair);
|
|
||||||
$key = (isset($keyvalueexploded[0]) ? $keyvalueexploded[0] : '');
|
|
||||||
$value = (isset($keyvalueexploded[1]) ? $keyvalueexploded[1] : '');
|
|
||||||
$info['svg'][$section_to_fix]['sections'][$key] = trim($value, '"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$info['fileformat'] = 'svg';
|
$sections_to_fix = array('xml', 'doctype', 'svg');
|
||||||
$info['video']['dataformat'] = 'svg';
|
foreach ($sections_to_fix as $section_to_fix) {
|
||||||
$info['video']['lossless'] = true;
|
if (!isset($info['svg'][$section_to_fix])) {
|
||||||
//$info['video']['bits_per_sample'] = 24;
|
continue;
|
||||||
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
}
|
||||||
|
$section_data = array();
|
||||||
|
while (preg_match('/ "([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
||||||
|
$section_data[] = $matches[1];
|
||||||
|
$info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]);
|
||||||
|
}
|
||||||
|
while (preg_match('/([^\s]+)="([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) {
|
||||||
|
$section_data[] = $matches[0];
|
||||||
|
$info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]);
|
||||||
|
}
|
||||||
|
$section_data = array_merge($section_data, preg_split('/[\s,]+/', $info['svg'][$section_to_fix]['raw'][1]));
|
||||||
|
foreach ($section_data as $keyvaluepair) {
|
||||||
|
$keyvaluepair = trim($keyvaluepair);
|
||||||
|
if ($keyvaluepair) {
|
||||||
|
$keyvalueexploded = explode('=', $keyvaluepair);
|
||||||
|
$key = (isset($keyvalueexploded[0]) ? $keyvalueexploded[0] : '');
|
||||||
|
$value = (isset($keyvalueexploded[1]) ? $keyvalueexploded[1] : '');
|
||||||
|
$info['svg'][$section_to_fix]['sections'][$key] = trim($value, '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($info['svg']['svg']['sections']['width'])) {
|
$info['fileformat'] = 'svg';
|
||||||
$info['svg']['width'] = intval($info['svg']['svg']['sections']['width']);
|
$info['video']['dataformat'] = 'svg';
|
||||||
}
|
$info['video']['lossless'] = true;
|
||||||
if (!empty($info['svg']['svg']['sections']['height'])) {
|
//$info['video']['bits_per_sample'] = 24;
|
||||||
$info['svg']['height'] = intval($info['svg']['svg']['sections']['height']);
|
$info['video']['pixel_aspect_ratio'] = (float) 1;
|
||||||
}
|
|
||||||
if (!empty($info['svg']['svg']['sections']['version'])) {
|
|
||||||
$info['svg']['version'] = $info['svg']['svg']['sections']['version'];
|
|
||||||
}
|
|
||||||
if (!isset($info['svg']['version']) && isset($info['svg']['doctype']['sections'])) {
|
|
||||||
foreach ($info['svg']['doctype']['sections'] as $key => $value) {
|
|
||||||
if (preg_match('#//W3C//DTD SVG ([0-9\.]+)//#i', $key, $matches)) {
|
|
||||||
$info['svg']['version'] = $matches[1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($info['svg']['width'])) {
|
if (!empty($info['svg']['svg']['sections']['width'])) {
|
||||||
$info['video']['resolution_x'] = $info['svg']['width'];
|
$info['svg']['width'] = intval($info['svg']['svg']['sections']['width']);
|
||||||
}
|
}
|
||||||
if (!empty($info['svg']['height'])) {
|
if (!empty($info['svg']['svg']['sections']['height'])) {
|
||||||
$info['video']['resolution_y'] = $info['svg']['height'];
|
$info['svg']['height'] = intval($info['svg']['svg']['sections']['height']);
|
||||||
}
|
}
|
||||||
|
if (!empty($info['svg']['svg']['sections']['version'])) {
|
||||||
|
$info['svg']['version'] = $info['svg']['svg']['sections']['version'];
|
||||||
|
}
|
||||||
|
if (!isset($info['svg']['version']) && isset($info['svg']['doctype']['sections'])) {
|
||||||
|
foreach ($info['svg']['doctype']['sections'] as $key => $value) {
|
||||||
|
if (preg_match('#//W3C//DTD SVG ([0-9\.]+)//#i', $key, $matches)) {
|
||||||
|
$info['svg']['version'] = $matches[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($info['svg']['width'])) {
|
||||||
|
$info['video']['resolution_x'] = $info['svg']['width'];
|
||||||
|
}
|
||||||
|
if (!empty($info['svg']['height'])) {
|
||||||
|
$info['video']['resolution_y'] = $info['svg']['height'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$this->error('Did not find expected <svg> tag');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$info['error'][] = 'Did not find expected <svg> tag';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,207 +18,208 @@
|
||||||
class getid3_tiff extends getid3_handler
|
class getid3_tiff extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$TIFFheader = $this->fread(4);
|
$TIFFheader = $this->fread(4);
|
||||||
|
|
||||||
switch (substr($TIFFheader, 0, 2)) {
|
switch (substr($TIFFheader, 0, 2)) {
|
||||||
case 'II':
|
case 'II':
|
||||||
$info['tiff']['byte_order'] = 'Intel';
|
$info['tiff']['byte_order'] = 'Intel';
|
||||||
break;
|
break;
|
||||||
case 'MM':
|
case 'MM':
|
||||||
$info['tiff']['byte_order'] = 'Motorola';
|
$info['tiff']['byte_order'] = 'Motorola';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$info['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset'];
|
$this->error('Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset']);
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['fileformat'] = 'tiff';
|
$info['fileformat'] = 'tiff';
|
||||||
$info['video']['dataformat'] = 'tiff';
|
$info['video']['dataformat'] = 'tiff';
|
||||||
$info['video']['lossless'] = true;
|
$info['video']['lossless'] = true;
|
||||||
$info['tiff']['ifd'] = [];
|
$info['tiff']['ifd'] = array();
|
||||||
$CurrentIFD = [];
|
$CurrentIFD = array();
|
||||||
|
|
||||||
$FieldTypeByteLength = [1=>1, 2=>1, 3=>2, 4=>4, 5=>8];
|
$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
|
||||||
|
|
||||||
$nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
$nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
||||||
|
|
||||||
while ($nextIFDoffset > 0) {
|
while ($nextIFDoffset > 0) {
|
||||||
$CurrentIFD['offset'] = $nextIFDoffset;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset'] + $nextIFDoffset);
|
$CurrentIFD['offset'] = $nextIFDoffset;
|
||||||
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
|
||||||
|
|
||||||
for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
|
$this->fseek($info['avdataoffset'] + $nextIFDoffset);
|
||||||
$CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
||||||
$CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
|
||||||
$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
|
||||||
$CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4);
|
|
||||||
|
|
||||||
switch ($CurrentIFD['fields'][$i]['raw']['type']) {
|
for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
|
||||||
case 1: // BYTE An 8-bit unsigned integer.
|
$CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
||||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
$CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
|
||||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']);
|
$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
||||||
} else {
|
$CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4);
|
||||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
|
switch ($CurrentIFD['fields'][$i]['raw']['type']) {
|
||||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
case 1: // BYTE An 8-bit unsigned integer.
|
||||||
$CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
|
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
||||||
} else {
|
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']);
|
||||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
} else {
|
||||||
}
|
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 3: // SHORT A 16-bit (2-byte) unsigned integer.
|
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
|
||||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
|
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
|
||||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']);
|
$CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
|
||||||
} else {
|
} else {
|
||||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: // LONG A 32-bit (4-byte) unsigned integer.
|
case 3: // SHORT A 16-bit (2-byte) unsigned integer.
|
||||||
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
|
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
|
||||||
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']);
|
||||||
} else {
|
} else {
|
||||||
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
|
case 4: // LONG A 32-bit (4-byte) unsigned integer.
|
||||||
break;
|
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
|
||||||
}
|
$CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
||||||
}
|
} else {
|
||||||
|
$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$info['tiff']['ifd'][] = $CurrentIFD;
|
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
|
||||||
$CurrentIFD = [];
|
break;
|
||||||
$nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($info['tiff']['ifd'] as $IFDid => $IFDarray) {
|
$info['tiff']['ifd'][] = $CurrentIFD;
|
||||||
foreach ($IFDarray['fields'] as $key => $fieldarray) {
|
$CurrentIFD = array();
|
||||||
switch ($fieldarray['raw']['tag']) {
|
$nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
|
||||||
case 256: // ImageWidth
|
|
||||||
case 257: // ImageLength
|
|
||||||
case 258: // BitsPerSample
|
|
||||||
case 259: // Compression
|
|
||||||
if (!isset($fieldarray['value'])) {
|
|
||||||
$this->fseek($fieldarray['offset']);
|
|
||||||
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 270: // ImageDescription
|
}
|
||||||
case 271: // Make
|
|
||||||
case 272: // Model
|
|
||||||
case 305: // Software
|
|
||||||
case 306: // DateTime
|
|
||||||
case 315: // Artist
|
|
||||||
case 316: // HostComputer
|
|
||||||
if (isset($fieldarray['value'])) {
|
|
||||||
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
|
|
||||||
} else {
|
|
||||||
$this->fseek($fieldarray['offset']);
|
|
||||||
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch ($fieldarray['raw']['tag']) {
|
|
||||||
case 256: // ImageWidth
|
|
||||||
$info['video']['resolution_x'] = $fieldarray['value'];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 257: // ImageLength
|
foreach ($info['tiff']['ifd'] as $IFDid => $IFDarray) {
|
||||||
$info['video']['resolution_y'] = $fieldarray['value'];
|
foreach ($IFDarray['fields'] as $key => $fieldarray) {
|
||||||
break;
|
switch ($fieldarray['raw']['tag']) {
|
||||||
|
case 256: // ImageWidth
|
||||||
|
case 257: // ImageLength
|
||||||
|
case 258: // BitsPerSample
|
||||||
|
case 259: // Compression
|
||||||
|
if (!isset($fieldarray['value'])) {
|
||||||
|
$this->fseek($fieldarray['offset']);
|
||||||
|
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
||||||
|
|
||||||
case 258: // BitsPerSample
|
}
|
||||||
if (isset($fieldarray['value'])) {
|
break;
|
||||||
$info['video']['bits_per_sample'] = $fieldarray['value'];
|
|
||||||
} else {
|
|
||||||
$info['video']['bits_per_sample'] = 0;
|
|
||||||
for ($i = 0; $i < $fieldarray['raw']['length']; $i++) {
|
|
||||||
$info['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $info['tiff']['byte_order']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 259: // Compression
|
case 270: // ImageDescription
|
||||||
$info['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
|
case 271: // Make
|
||||||
break;
|
case 272: // Model
|
||||||
|
case 305: // Software
|
||||||
|
case 306: // DateTime
|
||||||
|
case 315: // Artist
|
||||||
|
case 316: // HostComputer
|
||||||
|
if (isset($fieldarray['value'])) {
|
||||||
|
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
|
||||||
|
} else {
|
||||||
|
$this->fseek($fieldarray['offset']);
|
||||||
|
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
|
||||||
|
|
||||||
case 270: // ImageDescription
|
}
|
||||||
case 271: // Make
|
break;
|
||||||
case 272: // Model
|
}
|
||||||
case 305: // Software
|
switch ($fieldarray['raw']['tag']) {
|
||||||
case 306: // DateTime
|
case 256: // ImageWidth
|
||||||
case 315: // Artist
|
$info['video']['resolution_x'] = $fieldarray['value'];
|
||||||
case 316: // HostComputer
|
break;
|
||||||
$TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']);
|
|
||||||
if (isset($info['tiff']['comments'][$TIFFcommentName])) {
|
|
||||||
$info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
|
|
||||||
} else {
|
|
||||||
$info['tiff']['comments'][$TIFFcommentName] = [$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
case 257: // ImageLength
|
||||||
break;
|
$info['video']['resolution_y'] = $fieldarray['value'];
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
case 258: // BitsPerSample
|
||||||
}
|
if (isset($fieldarray['value'])) {
|
||||||
|
$info['video']['bits_per_sample'] = $fieldarray['value'];
|
||||||
|
} else {
|
||||||
|
$info['video']['bits_per_sample'] = 0;
|
||||||
|
for ($i = 0; $i < $fieldarray['raw']['length']; $i++) {
|
||||||
|
$info['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $info['tiff']['byte_order']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 259: // Compression
|
||||||
|
$info['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 270: // ImageDescription
|
||||||
|
case 271: // Make
|
||||||
|
case 272: // Model
|
||||||
|
case 305: // Software
|
||||||
|
case 306: // DateTime
|
||||||
|
case 315: // Artist
|
||||||
|
case 316: // HostComputer
|
||||||
|
$TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']);
|
||||||
|
if (isset($info['tiff']['comments'][$TIFFcommentName])) {
|
||||||
|
$info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
|
||||||
|
} else {
|
||||||
|
$info['tiff']['comments'][$TIFFcommentName] = array($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function TIFFendian2Int($bytestring, $byteorder)
|
public function TIFFendian2Int($bytestring, $byteorder) {
|
||||||
{
|
if ($byteorder == 'Intel') {
|
||||||
if ($byteorder == 'Intel') {
|
return getid3_lib::LittleEndian2Int($bytestring);
|
||||||
return getid3_lib::LittleEndian2Int($bytestring);
|
} elseif ($byteorder == 'Motorola') {
|
||||||
} elseif ($byteorder == 'Motorola') {
|
return getid3_lib::BigEndian2Int($bytestring);
|
||||||
return getid3_lib::BigEndian2Int($bytestring);
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function TIFFcompressionMethod($id)
|
public function TIFFcompressionMethod($id) {
|
||||||
{
|
static $TIFFcompressionMethod = array();
|
||||||
static $TIFFcompressionMethod = [];
|
if (empty($TIFFcompressionMethod)) {
|
||||||
if (empty($TIFFcompressionMethod)) {
|
$TIFFcompressionMethod = array(
|
||||||
$TIFFcompressionMethod = [
|
1 => 'Uncompressed',
|
||||||
1 => 'Uncompressed',
|
2 => 'Huffman',
|
||||||
2 => 'Huffman',
|
3 => 'Fax - CCITT 3',
|
||||||
3 => 'Fax - CCITT 3',
|
5 => 'LZW',
|
||||||
5 => 'LZW',
|
32773 => 'PackBits',
|
||||||
32773 => 'PackBits',
|
);
|
||||||
];
|
}
|
||||||
}
|
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
|
||||||
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
|
}
|
||||||
}
|
|
||||||
|
public function TIFFcommentName($id) {
|
||||||
|
static $TIFFcommentName = array();
|
||||||
|
if (empty($TIFFcommentName)) {
|
||||||
|
$TIFFcommentName = array(
|
||||||
|
270 => 'imagedescription',
|
||||||
|
271 => 'make',
|
||||||
|
272 => 'model',
|
||||||
|
305 => 'software',
|
||||||
|
306 => 'datetime',
|
||||||
|
315 => 'artist',
|
||||||
|
316 => 'hostcomputer',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
|
||||||
|
}
|
||||||
|
|
||||||
public function TIFFcommentName($id)
|
|
||||||
{
|
|
||||||
static $TIFFcommentName = [];
|
|
||||||
if (empty($TIFFcommentName)) {
|
|
||||||
$TIFFcommentName = [
|
|
||||||
270 => 'imagedescription',
|
|
||||||
271 => 'make',
|
|
||||||
272 => 'model',
|
|
||||||
305 => 'software',
|
|
||||||
306 => 'datetime',
|
|
||||||
315 => 'artist',
|
|
||||||
316 => 'hostcomputer',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,262 +33,280 @@
|
||||||
*/
|
*/
|
||||||
class getid3_cue extends getid3_handler
|
class getid3_cue extends getid3_handler
|
||||||
{
|
{
|
||||||
public $cuesheet = [];
|
public $cuesheet = array();
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'cue';
|
$info['fileformat'] = 'cue';
|
||||||
$this->readCueSheetFilename($info['filenamepath']);
|
$this->readCueSheetFilename($info['filenamepath']);
|
||||||
$info['cue'] = $this->cuesheet;
|
$info['cue'] = $this->cuesheet;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function readCueSheetFilename($filename)
|
public function readCueSheetFilename($filename)
|
||||||
{
|
{
|
||||||
$filedata = file_get_contents($filename);
|
$filedata = file_get_contents($filename);
|
||||||
return $this->readCueSheet($filedata);
|
return $this->readCueSheet($filedata);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Parses a cue sheet file.
|
* Parses a cue sheet file.
|
||||||
*
|
*
|
||||||
* @param string $filename - The filename for the cue sheet to open.
|
* @param string $filename - The filename for the cue sheet to open.
|
||||||
*/
|
*/
|
||||||
public function readCueSheet(&$filedata)
|
public function readCueSheet(&$filedata)
|
||||||
{
|
{
|
||||||
$cue_lines = [];
|
$cue_lines = array();
|
||||||
foreach (explode("\n", str_replace("\r", null, $filedata)) as $line) {
|
foreach (explode("\n", str_replace("\r", null, $filedata)) as $line)
|
||||||
if ((strlen($line) > 0) && ($line[0] != '#')) {
|
{
|
||||||
$cue_lines[] = trim($line);
|
if ( (strlen($line) > 0) && ($line[0] != '#'))
|
||||||
}
|
{
|
||||||
}
|
$cue_lines[] = trim($line);
|
||||||
$this->parseCueSheet($cue_lines);
|
}
|
||||||
|
}
|
||||||
|
$this->parseCueSheet($cue_lines);
|
||||||
|
|
||||||
return $this->cuesheet;
|
return $this->cuesheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the cue sheet array.
|
* Parses the cue sheet array.
|
||||||
*
|
*
|
||||||
* @param array $file - The cuesheet as an array of each line.
|
* @param array $file - The cuesheet as an array of each line.
|
||||||
*/
|
*/
|
||||||
public function parseCueSheet($file)
|
public function parseCueSheet($file)
|
||||||
{
|
{
|
||||||
//-1 means still global, all others are track specific
|
//-1 means still global, all others are track specific
|
||||||
$track_on = -1;
|
$track_on = -1;
|
||||||
|
|
||||||
for ($i=0; $i < count($file); $i++) {
|
for ($i=0; $i < count($file); $i++)
|
||||||
list($key) = explode(' ', strtolower($file[$i]), 2);
|
{
|
||||||
switch ($key) {
|
list($key) = explode(' ', strtolower($file[$i]), 2);
|
||||||
case 'catalog':
|
switch ($key)
|
||||||
case 'cdtextfile':
|
{
|
||||||
case 'isrc':
|
case 'catalog':
|
||||||
case 'performer':
|
case 'cdtextfile':
|
||||||
case 'songwriter':
|
case 'isrc':
|
||||||
case 'title':
|
case 'performer':
|
||||||
$this->parseString($file[$i], $track_on);
|
case 'songwriter':
|
||||||
break;
|
case 'title':
|
||||||
case 'file':
|
$this->parseString($file[$i], $track_on);
|
||||||
$currentFile = $this->parseFile($file[$i]);
|
break;
|
||||||
break;
|
case 'file':
|
||||||
case 'flags':
|
$currentFile = $this->parseFile($file[$i]);
|
||||||
$this->parseFlags($file[$i], $track_on);
|
break;
|
||||||
break;
|
case 'flags':
|
||||||
case 'index':
|
$this->parseFlags($file[$i], $track_on);
|
||||||
case 'postgap':
|
break;
|
||||||
case 'pregap':
|
case 'index':
|
||||||
$this->parseIndex($file[$i], $track_on);
|
case 'postgap':
|
||||||
break;
|
case 'pregap':
|
||||||
case 'rem':
|
$this->parseIndex($file[$i], $track_on);
|
||||||
$this->parseComment($file[$i], $track_on);
|
break;
|
||||||
break;
|
case 'rem':
|
||||||
case 'track':
|
$this->parseComment($file[$i], $track_on);
|
||||||
$track_on++;
|
break;
|
||||||
$this->parseTrack($file[$i], $track_on);
|
case 'track':
|
||||||
if (isset($currentFile)) { // if there's a file
|
$track_on++;
|
||||||
$this->cuesheet['tracks'][$track_on]['datafile'] = $currentFile;
|
$this->parseTrack($file[$i], $track_on);
|
||||||
}
|
if (isset($currentFile)) // if there's a file
|
||||||
break;
|
{
|
||||||
default:
|
$this->cuesheet['tracks'][$track_on]['datafile'] = $currentFile;
|
||||||
//save discarded junk and place string[] with track it was found in
|
}
|
||||||
$this->parseGarbage($file[$i], $track_on);
|
break;
|
||||||
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.
|
* Parses the REM command.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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.
|
* @param integer $track_on - The track currently processing.
|
||||||
*/
|
*/
|
||||||
public function parseComment($line, $track_on)
|
public function parseComment($line, $track_on)
|
||||||
{
|
{
|
||||||
$explodedline = explode(' ', $line, 3);
|
$explodedline = explode(' ', $line, 3);
|
||||||
$comment_REM = (isset($explodedline[0]) ? $explodedline[0] : '');
|
$comment_REM = (isset($explodedline[0]) ? $explodedline[0] : '');
|
||||||
$comment_type = (isset($explodedline[1]) ? $explodedline[1] : '');
|
$comment_type = (isset($explodedline[1]) ? $explodedline[1] : '');
|
||||||
$comment_data = (isset($explodedline[2]) ? $explodedline[2] : '');
|
$comment_data = (isset($explodedline[2]) ? $explodedline[2] : '');
|
||||||
if (($comment_REM == 'REM') && $comment_type) {
|
if (($comment_REM == 'REM') && $comment_type) {
|
||||||
$comment_type = strtolower($comment_type);
|
$comment_type = strtolower($comment_type);
|
||||||
$commment_data = trim($comment_data, ' "');
|
$commment_data = trim($comment_data, ' "');
|
||||||
if ($track_on != -1) {
|
if ($track_on != -1) {
|
||||||
$this->cuesheet['tracks'][$track_on]['comments'][$comment_type][] = $comment_data;
|
$this->cuesheet['tracks'][$track_on]['comments'][$comment_type][] = $comment_data;
|
||||||
} else {
|
} else {
|
||||||
$this->cuesheet['comments'][$comment_type][] = $comment_data;
|
$this->cuesheet['comments'][$comment_type][] = $comment_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the FILE command.
|
* Parses the FILE command.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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..
|
* @return array - Array of FILENAME and TYPE of file..
|
||||||
*/
|
*/
|
||||||
public function parseFile($line)
|
public function parseFile($line)
|
||||||
{
|
{
|
||||||
$line = substr($line, strpos($line, ' ') + 1);
|
$line = substr($line, strpos($line, ' ') + 1);
|
||||||
$type = strtolower(substr($line, strrpos($line, ' ')));
|
$type = strtolower(substr($line, strrpos($line, ' ')));
|
||||||
|
|
||||||
//remove type
|
//remove type
|
||||||
$line = substr($line, 0, strrpos($line, ' ') - 1);
|
$line = substr($line, 0, strrpos($line, ' ') - 1);
|
||||||
|
|
||||||
//if quotes around it, remove them.
|
//if quotes around it, remove them.
|
||||||
$line = trim($line, '"');
|
$line = trim($line, '"');
|
||||||
|
|
||||||
return ['filename'=>$line, 'type'=>$type];
|
return array('filename'=>$line, 'type'=>$type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the FLAG command.
|
* Parses the FLAG command.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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.
|
* @param integer $track_on - The track currently processing.
|
||||||
*/
|
*/
|
||||||
public function parseFlags($line, $track_on)
|
public function parseFlags($line, $track_on)
|
||||||
{
|
{
|
||||||
if ($track_on != -1) {
|
if ($track_on != -1)
|
||||||
foreach (explode(' ', strtolower($line)) as $type) {
|
{
|
||||||
switch ($type) {
|
foreach (explode(' ', strtolower($line)) as $type)
|
||||||
case 'flags':
|
{
|
||||||
// first entry in this line
|
switch ($type)
|
||||||
$this->cuesheet['tracks'][$track_on]['flags'] = [
|
{
|
||||||
'4ch' => false,
|
case 'flags':
|
||||||
'data' => false,
|
// first entry in this line
|
||||||
'dcp' => false,
|
$this->cuesheet['tracks'][$track_on]['flags'] = array(
|
||||||
'pre' => false,
|
'4ch' => false,
|
||||||
'scms' => false,
|
'data' => false,
|
||||||
];
|
'dcp' => false,
|
||||||
break;
|
'pre' => false,
|
||||||
case 'data':
|
'scms' => false,
|
||||||
case 'dcp':
|
);
|
||||||
case '4ch':
|
break;
|
||||||
case 'pre':
|
case 'data':
|
||||||
case 'scms':
|
case 'dcp':
|
||||||
$this->cuesheet['tracks'][$track_on]['flags'][$type] = true;
|
case '4ch':
|
||||||
break;
|
case 'pre':
|
||||||
default:
|
case 'scms':
|
||||||
break;
|
$this->cuesheet['tracks'][$track_on]['flags'][$type] = true;
|
||||||
}
|
break;
|
||||||
}
|
default:
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect any unidentified data.
|
* Collect any unidentified data.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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.
|
* @param integer $track_on - The track currently processing.
|
||||||
*/
|
*/
|
||||||
public function parseGarbage($line, $track_on)
|
public function parseGarbage($line, $track_on)
|
||||||
{
|
{
|
||||||
if (strlen($line) > 0) {
|
if ( strlen($line) > 0 )
|
||||||
if ($track_on == -1) {
|
{
|
||||||
$this->cuesheet['garbage'][] = $line;
|
if ($track_on == -1)
|
||||||
} else {
|
{
|
||||||
$this->cuesheet['tracks'][$track_on]['garbage'][] = $line;
|
$this->cuesheet['garbage'][] = $line;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
$this->cuesheet['tracks'][$track_on]['garbage'][] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the INDEX command of a TRACK.
|
* Parses the INDEX command of a TRACK.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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.
|
* @param integer $track_on - The track currently processing.
|
||||||
*/
|
*/
|
||||||
public function parseIndex($line, $track_on)
|
public function parseIndex($line, $track_on)
|
||||||
{
|
{
|
||||||
$type = strtolower(substr($line, 0, strpos($line, ' ')));
|
$type = strtolower(substr($line, 0, strpos($line, ' ')));
|
||||||
$line = substr($line, strpos($line, ' ') + 1);
|
$line = substr($line, strpos($line, ' ') + 1);
|
||||||
|
|
||||||
if ($type == 'index') {
|
if ($type == 'index')
|
||||||
//read the index number
|
{
|
||||||
$number = intval(substr($line, 0, strpos($line, ' ')));
|
//read the index number
|
||||||
$line = substr($line, strpos($line, ' ') + 1);
|
$number = intval(substr($line, 0, strpos($line, ' ')));
|
||||||
}
|
$line = substr($line, strpos($line, ' ') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
//extract the minutes, seconds, and frames
|
//extract the minutes, seconds, and frames
|
||||||
$explodedline = explode(':', $line);
|
$explodedline = explode(':', $line);
|
||||||
$minutes = (isset($explodedline[0]) ? $explodedline[0] : '');
|
$minutes = (isset($explodedline[0]) ? $explodedline[0] : '');
|
||||||
$seconds = (isset($explodedline[1]) ? $explodedline[1] : '');
|
$seconds = (isset($explodedline[1]) ? $explodedline[1] : '');
|
||||||
$frames = (isset($explodedline[2]) ? $explodedline[2] : '');
|
$frames = (isset($explodedline[2]) ? $explodedline[2] : '');
|
||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'index':
|
case 'index':
|
||||||
$this->cuesheet['tracks'][$track_on][$type][$number] = ['minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames)];
|
$this->cuesheet['tracks'][$track_on][$type][$number] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
|
||||||
break;
|
break;
|
||||||
case 'pregap':
|
case 'pregap':
|
||||||
case 'postgap':
|
case 'postgap':
|
||||||
$this->cuesheet['tracks'][$track_on][$type] = ['minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames)];
|
$this->cuesheet['tracks'][$track_on][$type] = array('minutes'=>intval($minutes), 'seconds'=>intval($seconds), 'frames'=>intval($frames));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseString($line, $track_on)
|
public function parseString($line, $track_on)
|
||||||
{
|
{
|
||||||
$category = strtolower(substr($line, 0, strpos($line, ' ')));
|
$category = strtolower(substr($line, 0, strpos($line, ' ')));
|
||||||
$line = substr($line, strpos($line, ' ') + 1);
|
$line = substr($line, strpos($line, ' ') + 1);
|
||||||
|
|
||||||
//get rid of the quotes
|
//get rid of the quotes
|
||||||
$line = trim($line, '"');
|
$line = trim($line, '"');
|
||||||
|
|
||||||
switch ($category) {
|
switch ($category)
|
||||||
case 'catalog':
|
{
|
||||||
case 'cdtextfile':
|
case 'catalog':
|
||||||
case 'isrc':
|
case 'cdtextfile':
|
||||||
case 'performer':
|
case 'isrc':
|
||||||
case 'songwriter':
|
case 'performer':
|
||||||
case 'title':
|
case 'songwriter':
|
||||||
if ($track_on == -1) {
|
case 'title':
|
||||||
$this->cuesheet[$category] = $line;
|
if ($track_on == -1)
|
||||||
} else {
|
{
|
||||||
$this->cuesheet['tracks'][$track_on][$category] = $line;
|
$this->cuesheet[$category] = $line;
|
||||||
}
|
}
|
||||||
break;
|
else
|
||||||
default:
|
{
|
||||||
break;
|
$this->cuesheet['tracks'][$track_on][$category] = $line;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the TRACK command.
|
* Parses the TRACK command.
|
||||||
*
|
*
|
||||||
* @param string $line - The line in the cue file that contains 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.
|
* @param integer $track_on - The track currently processing.
|
||||||
*/
|
*/
|
||||||
public function parseTrack($line, $track_on)
|
public function parseTrack($line, $track_on)
|
||||||
{
|
{
|
||||||
$line = substr($line, strpos($line, ' ') + 1);
|
$line = substr($line, strpos($line, ' ') + 1);
|
||||||
$track = ltrim(substr($line, 0, strpos($line, ' ')), '0');
|
$track = ltrim(substr($line, 0, strpos($line, ' ')), '0');
|
||||||
|
|
||||||
//find the data type.
|
//find the data type.
|
||||||
$datatype = strtolower(substr($line, strpos($line, ' ') + 1));
|
$datatype = strtolower(substr($line, strpos($line, ' ') + 1));
|
||||||
|
|
||||||
|
$this->cuesheet['tracks'][$track_on] = array('track_number'=>$track, 'datatype'=>$datatype);
|
||||||
|
}
|
||||||
|
|
||||||
$this->cuesheet['tracks'][$track_on] = ['track_number'=>$track, 'datatype'=>$datatype];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,41 +18,42 @@
|
||||||
class getid3_exe extends getid3_handler
|
class getid3_exe extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$EXEheader = $this->fread(28);
|
$EXEheader = $this->fread(28);
|
||||||
|
|
||||||
$magic = 'MZ';
|
$magic = 'MZ';
|
||||||
if (substr($EXEheader, 0, 2) != $magic) {
|
if (substr($EXEheader, 0, 2) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($EXEheader, 0, 2)).'"';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($EXEheader, 0, 2)).'"');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['fileformat'] = 'exe';
|
$info['fileformat'] = 'exe';
|
||||||
$info['exe']['mz']['magic'] = 'MZ';
|
$info['exe']['mz']['magic'] = 'MZ';
|
||||||
|
|
||||||
$info['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
|
$info['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2));
|
||||||
$info['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
|
$info['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2));
|
||||||
$info['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
|
$info['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2));
|
||||||
$info['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
|
$info['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2));
|
||||||
$info['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
|
$info['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
|
||||||
$info['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
|
$info['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
|
||||||
$info['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
|
$info['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
|
||||||
$info['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
|
$info['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
|
||||||
$info['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
|
$info['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
|
||||||
$info['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
|
$info['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
|
||||||
$info['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
|
$info['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
|
||||||
$info['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
|
$info['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
|
||||||
|
|
||||||
$info['exe']['mz']['byte_size'] = (($info['exe']['mz']['raw']['page_count'] - 1)) * 512 + $info['exe']['mz']['raw']['last_page_size'];
|
$info['exe']['mz']['byte_size'] = (($info['exe']['mz']['raw']['page_count'] - 1)) * 512 + $info['exe']['mz']['raw']['last_page_size'];
|
||||||
$info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16;
|
$info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16;
|
||||||
$info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
|
$info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
|
||||||
$info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
|
$info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
|
||||||
|
|
||||||
|
$this->error('EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$info['error'][] = 'EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,376 +18,371 @@
|
||||||
class getid3_iso extends getid3_handler
|
class getid3_iso extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'iso';
|
$info['fileformat'] = 'iso';
|
||||||
|
|
||||||
for ($i = 16; $i <= 19; $i++) {
|
for ($i = 16; $i <= 19; $i++) {
|
||||||
$this->fseek(2048 * $i);
|
$this->fseek(2048 * $i);
|
||||||
$ISOheader = $this->fread(2048);
|
$ISOheader = $this->fread(2048);
|
||||||
if (substr($ISOheader, 1, 5) == 'CD001') {
|
if (substr($ISOheader, 1, 5) == 'CD001') {
|
||||||
switch (ord($ISOheader{0})) {
|
switch (ord($ISOheader{0})) {
|
||||||
case 1:
|
case 1:
|
||||||
$info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
|
$info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
|
||||||
$this->ParsePrimaryVolumeDescriptor($ISOheader);
|
$this->ParsePrimaryVolumeDescriptor($ISOheader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
$info['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
|
$info['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
|
||||||
$this->ParseSupplementaryVolumeDescriptor($ISOheader);
|
$this->ParseSupplementaryVolumeDescriptor($ISOheader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// skip
|
// skip
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ParsePathTable();
|
$this->ParsePathTable();
|
||||||
|
|
||||||
$info['iso']['files'] = [];
|
$info['iso']['files'] = array();
|
||||||
foreach ($info['iso']['path_table']['directories'] as $directorynum => $directorydata) {
|
foreach ($info['iso']['path_table']['directories'] as $directorynum => $directorydata) {
|
||||||
$info['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($directorydata);
|
$info['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($directorydata);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function ParsePrimaryVolumeDescriptor(&$ISOheader)
|
public function ParsePrimaryVolumeDescriptor(&$ISOheader) {
|
||||||
{
|
// ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!!
|
||||||
// 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
|
||||||
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
|
|
||||||
|
|
||||||
// shortcuts
|
// shortcuts
|
||||||
$info = &$this->getid3->info;
|
$info = &$this->getid3->info;
|
||||||
$info['iso']['primary_volume_descriptor']['raw'] = [];
|
$info['iso']['primary_volume_descriptor']['raw'] = array();
|
||||||
$thisfile_iso_primaryVD = &$info['iso']['primary_volume_descriptor'];
|
$thisfile_iso_primaryVD = &$info['iso']['primary_volume_descriptor'];
|
||||||
$thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw'];
|
$thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw'];
|
||||||
|
|
||||||
$thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
$thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
||||||
$thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
$thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
||||||
if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') {
|
if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') {
|
||||||
$info['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead';
|
$this->error('Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['iso']);
|
unset($info['iso']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
$thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
||||||
//$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
//$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
||||||
$thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
$thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
||||||
$thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
$thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
||||||
//$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
//$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
||||||
$thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
$thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
||||||
//$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
//$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
||||||
$thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
$thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
||||||
$thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
$thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
||||||
$thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
$thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
||||||
$thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
$thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
||||||
$thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
$thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
||||||
$thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
$thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
||||||
$thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
$thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
||||||
$thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
$thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
||||||
$thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
$thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
||||||
$thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
$thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
||||||
$thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
$thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
||||||
$thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
$thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
||||||
$thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
$thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
||||||
$thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
$thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
||||||
$thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
$thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
||||||
$thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
$thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
||||||
$thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
$thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
||||||
$thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
$thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
||||||
$thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
$thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
||||||
$thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
$thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
||||||
$thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
$thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
||||||
//$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
//$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
||||||
$thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
$thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
||||||
//$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
//$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
||||||
|
|
||||||
$thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']);
|
$thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']);
|
||||||
$thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
|
$thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
|
||||||
$thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
|
$thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
|
||||||
$thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
|
$thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
|
||||||
$thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
|
$thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
|
||||||
$thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']);
|
$thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']);
|
||||||
$thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
|
$thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
|
||||||
$thisfile_iso_primaryVD['abstract_file_identifier'] = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']);
|
$thisfile_iso_primaryVD['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['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_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']);
|
||||||
$thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']);
|
$thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']);
|
||||||
$thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']);
|
$thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']);
|
||||||
$thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']);
|
$thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']);
|
||||||
|
|
||||||
if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $info['filesize']) {
|
if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $info['filesize']) {
|
||||||
$info['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)';
|
$this->error('Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function ParseSupplementaryVolumeDescriptor(&$ISOheader)
|
public function ParseSupplementaryVolumeDescriptor(&$ISOheader) {
|
||||||
{
|
// ISO integer values are stored Both-Endian format!!
|
||||||
// ISO integer values are stored Both-Endian format!!
|
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
|
||||||
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
|
|
||||||
|
|
||||||
// shortcuts
|
// shortcuts
|
||||||
$info = &$this->getid3->info;
|
$info = &$this->getid3->info;
|
||||||
$info['iso']['supplementary_volume_descriptor']['raw'] = [];
|
$info['iso']['supplementary_volume_descriptor']['raw'] = array();
|
||||||
$thisfile_iso_supplementaryVD = &$info['iso']['supplementary_volume_descriptor'];
|
$thisfile_iso_supplementaryVD = &$info['iso']['supplementary_volume_descriptor'];
|
||||||
$thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw'];
|
$thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw'];
|
||||||
|
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
$thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1));
|
||||||
$thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
$thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5);
|
||||||
if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') {
|
if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') {
|
||||||
$info['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead';
|
$this->error('Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead');
|
||||||
unset($info['fileformat']);
|
unset($info['fileformat']);
|
||||||
unset($info['iso']);
|
unset($info['iso']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
$thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1));
|
||||||
//$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
//$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1);
|
||||||
$thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
$thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
$thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32);
|
||||||
//$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
//$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
$thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4));
|
||||||
if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
|
if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
|
||||||
// Supplementary Volume Descriptor not used
|
// Supplementary Volume Descriptor not used
|
||||||
//unset($thisfile_iso_supplementaryVD);
|
//unset($thisfile_iso_supplementaryVD);
|
||||||
//return false;
|
//return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
//$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
$thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
$thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
$thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
$thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4));
|
||||||
$thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
$thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
$thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
$thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
$thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2));
|
||||||
$thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
$thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
$thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128);
|
||||||
$thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
$thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128);
|
||||||
$thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
$thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128);
|
||||||
$thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
$thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128);
|
||||||
$thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
$thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37);
|
||||||
$thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
$thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37);
|
||||||
$thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
$thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
$thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
$thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
$thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17);
|
||||||
$thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
$thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17);
|
||||||
$thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
$thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1));
|
||||||
//$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
//$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1));
|
||||||
$thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
$thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512);
|
||||||
//$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
//$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653);
|
||||||
|
|
||||||
$thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
|
$thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
|
$thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
|
$thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
|
$thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
|
$thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
|
$thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
|
$thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
|
$thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
|
$thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
|
||||||
$thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
|
$thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
|
||||||
$thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
|
$thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
|
||||||
$thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']);
|
$thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']);
|
||||||
$thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']);
|
$thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']);
|
||||||
|
|
||||||
if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $info['filesize']) {
|
if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $info['filesize']) {
|
||||||
$info['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)';
|
$this->error('Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$info['filesize'].' bytes) (truncated file?)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function ParsePathTable()
|
public function ParsePathTable() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
if (!isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
|
||||||
if (!isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
if (isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) {
|
||||||
if (isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) {
|
$PathTableLocation = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'];
|
||||||
$PathTableLocation = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'];
|
$PathTableSize = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
|
||||||
$PathTableSize = $info['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
|
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
||||||
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
} else {
|
||||||
} else {
|
$PathTableLocation = $info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'];
|
||||||
$PathTableLocation = $info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'];
|
$PathTableSize = $info['iso']['primary_volume_descriptor']['raw']['path_table_size'];
|
||||||
$PathTableSize = $info['iso']['primary_volume_descriptor']['raw']['path_table_size'];
|
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
||||||
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (($PathTableLocation * 2048) > $info['filesize']) {
|
if (($PathTableLocation * 2048) > $info['filesize']) {
|
||||||
$info['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$info['filesize'].')';
|
$this->error('Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$info['filesize'].')');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['iso']['path_table']['offset'] = $PathTableLocation * 2048;
|
$info['iso']['path_table']['offset'] = $PathTableLocation * 2048;
|
||||||
$this->fseek($info['iso']['path_table']['offset']);
|
$this->fseek($info['iso']['path_table']['offset']);
|
||||||
$info['iso']['path_table']['raw'] = $this->fread($PathTableSize);
|
$info['iso']['path_table']['raw'] = $this->fread($PathTableSize);
|
||||||
|
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$pathcounter = 1;
|
$pathcounter = 1;
|
||||||
while ($offset < $PathTableSize) {
|
while ($offset < $PathTableSize) {
|
||||||
// shortcut
|
// shortcut
|
||||||
$info['iso']['path_table']['directories'][$pathcounter] = [];
|
$info['iso']['path_table']['directories'][$pathcounter] = array();
|
||||||
$thisfile_iso_pathtable_directories_current = &$info['iso']['path_table']['directories'][$pathcounter];
|
$thisfile_iso_pathtable_directories_current = &$info['iso']['path_table']['directories'][$pathcounter];
|
||||||
|
|
||||||
$thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1));
|
$thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1));
|
$thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 1));
|
||||||
$offset += 1;
|
$offset += 1;
|
||||||
$thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 4));
|
$thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 4));
|
||||||
$offset += 4;
|
$offset += 4;
|
||||||
$thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 2));
|
$thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($info['iso']['path_table']['raw'], $offset, 2));
|
||||||
$offset += 2;
|
$offset += 2;
|
||||||
$thisfile_iso_pathtable_directories_current['name'] = substr($info['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']);
|
$thisfile_iso_pathtable_directories_current['name'] = substr($info['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']);
|
||||||
$offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2);
|
$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, $info['encoding'], $thisfile_iso_pathtable_directories_current['name']);
|
$thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $info['encoding'], $thisfile_iso_pathtable_directories_current['name']);
|
||||||
|
|
||||||
$thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048;
|
$thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048;
|
||||||
if ($pathcounter == 1) {
|
if ($pathcounter == 1) {
|
||||||
$thisfile_iso_pathtable_directories_current['full_path'] = '/';
|
$thisfile_iso_pathtable_directories_current['full_path'] = '/';
|
||||||
} else {
|
} else {
|
||||||
$thisfile_iso_pathtable_directories_current['full_path'] = $info['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/';
|
$thisfile_iso_pathtable_directories_current['full_path'] = $info['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/';
|
||||||
}
|
}
|
||||||
$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
|
$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
|
||||||
|
|
||||||
$pathcounter++;
|
$pathcounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function ParseDirectoryRecord($directorydata)
|
public function ParseDirectoryRecord($directorydata) {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
if (isset($info['iso']['supplementary_volume_descriptor'])) {
|
||||||
if (isset($info['iso']['supplementary_volume_descriptor'])) {
|
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
||||||
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
|
} else {
|
||||||
} else {
|
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
||||||
$TextEncoding = 'ISO-8859-1'; // Latin-1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$this->fseek($directorydata['location_bytes']);
|
$this->fseek($directorydata['location_bytes']);
|
||||||
$DirectoryRecordData = $this->fread(1);
|
$DirectoryRecordData = $this->fread(1);
|
||||||
|
|
||||||
while (ord($DirectoryRecordData{0}) > 33) {
|
while (ord($DirectoryRecordData{0}) > 33) {
|
||||||
$DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1);
|
|
||||||
|
|
||||||
$ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
|
$DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1);
|
||||||
$ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
|
|
||||||
$ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
|
|
||||||
$ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
|
|
||||||
$ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
|
|
||||||
$ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
|
|
||||||
$ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
|
|
||||||
$ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
|
|
||||||
$ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
|
|
||||||
$ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1));
|
|
||||||
$ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']);
|
|
||||||
|
|
||||||
$ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $info['encoding'], $ThisDirectoryRecord['raw']['file_identifier']);
|
$ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
|
||||||
|
$ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
|
||||||
|
$ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
|
||||||
|
$ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
|
||||||
|
$ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1));
|
||||||
|
$ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']);
|
||||||
|
|
||||||
$ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize'];
|
$ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $info['encoding'], $ThisDirectoryRecord['raw']['file_identifier']);
|
||||||
$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['filesize'] = $ThisDirectoryRecord['raw']['filesize'];
|
||||||
$ThisDirectoryRecord['filename'] = $directorydata['full_path'];
|
$ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048;
|
||||||
} else {
|
$ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01);
|
||||||
$ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']);
|
$ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02);
|
||||||
$info['iso']['files'] = getid3_lib::array_merge_clobber($info['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize']));
|
$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']);
|
||||||
|
|
||||||
$DirectoryRecord[] = $ThisDirectoryRecord;
|
if ($ThisDirectoryRecord['file_flags']['directory']) {
|
||||||
$DirectoryRecordData = $this->fread(1);
|
$ThisDirectoryRecord['filename'] = $directorydata['full_path'];
|
||||||
}
|
} else {
|
||||||
|
$ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']);
|
||||||
|
$info['iso']['files'] = getid3_lib::array_merge_clobber($info['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize']));
|
||||||
|
}
|
||||||
|
|
||||||
return $DirectoryRecord;
|
$DirectoryRecord[] = $ThisDirectoryRecord;
|
||||||
}
|
$DirectoryRecordData = $this->fread(1);
|
||||||
|
}
|
||||||
|
|
||||||
public function ISOstripFilenameVersion($ISOfilename)
|
return $DirectoryRecord;
|
||||||
{
|
}
|
||||||
// convert 'filename.ext;1' to 'filename.ext'
|
|
||||||
if (!strstr($ISOfilename, ';')) {
|
|
||||||
return $ISOfilename;
|
|
||||||
} else {
|
|
||||||
return substr($ISOfilename, 0, strpos($ISOfilename, ';'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ISOtimeText2UNIXtime($ISOtime)
|
public function ISOstripFilenameVersion($ISOfilename) {
|
||||||
{
|
// convert 'filename.ext;1' to 'filename.ext'
|
||||||
|
if (!strstr($ISOfilename, ';')) {
|
||||||
|
return $ISOfilename;
|
||||||
|
} else {
|
||||||
|
return substr($ISOfilename, 0, strpos($ISOfilename, ';'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$UNIXyear = (int) substr($ISOtime, 0, 4);
|
public function ISOtimeText2UNIXtime($ISOtime) {
|
||||||
$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) {
|
$UNIXyear = (int) substr($ISOtime, 0, 4);
|
||||||
return false;
|
$UNIXmonth = (int) substr($ISOtime, 4, 2);
|
||||||
}
|
$UNIXday = (int) substr($ISOtime, 6, 2);
|
||||||
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
$UNIXhour = (int) substr($ISOtime, 8, 2);
|
||||||
}
|
$UNIXminute = (int) substr($ISOtime, 10, 2);
|
||||||
|
$UNIXsecond = (int) substr($ISOtime, 12, 2);
|
||||||
|
|
||||||
public function ISOtime2UNIXtime($ISOtime)
|
if (!$UNIXyear) {
|
||||||
{
|
return false;
|
||||||
// Represented by seven bytes:
|
}
|
||||||
// 1: Number of years since 1900
|
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
||||||
// 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;
|
public function ISOtime2UNIXtime($ISOtime) {
|
||||||
$UNIXmonth = ord($ISOtime{1});
|
// Represented by seven bytes:
|
||||||
$UNIXday = ord($ISOtime{2});
|
// 1: Number of years since 1900
|
||||||
$UNIXhour = ord($ISOtime{3});
|
// 2: Month of the year from 1 to 12
|
||||||
$UNIXminute = ord($ISOtime{4});
|
// 3: Day of the Month from 1 to 31
|
||||||
$UNIXsecond = ord($ISOtime{5});
|
// 4: Hour of the day from 0 to 23
|
||||||
$GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
|
// 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($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,20 +18,21 @@
|
||||||
class getid3_msoffice extends getid3_handler
|
class getid3_msoffice extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$this->fseek($info['avdataoffset']);
|
$this->fseek($info['avdataoffset']);
|
||||||
$DOCFILEheader = $this->fread(8);
|
$DOCFILEheader = $this->fread(8);
|
||||||
$magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1";
|
$magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1";
|
||||||
if (substr($DOCFILEheader, 0, 8) != $magic) {
|
if (substr($DOCFILEheader, 0, 8) != $magic) {
|
||||||
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.';
|
$this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$info['fileformat'] = 'msoffice';
|
$info['fileformat'] = 'msoffice';
|
||||||
|
|
||||||
|
$this->error('MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$info['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
class getid3_par2 extends getid3_handler
|
class getid3_par2 extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'par2';
|
$info['fileformat'] = 'par2';
|
||||||
|
|
||||||
|
$this->error('PAR2 parsing not enabled in this version of getID3()');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$info['error'][] = 'PAR2 parsing not enabled in this version of getID3()';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
class getid3_pdf extends getid3_handler
|
class getid3_pdf extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
$info['fileformat'] = 'pdf';
|
$info['fileformat'] = 'pdf';
|
||||||
|
|
||||||
|
$this->error('PDF parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$info['error'][] = 'PDF parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,395 +16,401 @@
|
||||||
|
|
||||||
class getid3_apetag extends getid3_handler
|
class getid3_apetag extends getid3_handler
|
||||||
{
|
{
|
||||||
public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
|
public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
|
||||||
public $overrideendoffset = 0;
|
public $overrideendoffset = 0;
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
||||||
$info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
$this->warning('Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id3v1tagsize = 128;
|
$id3v1tagsize = 128;
|
||||||
$apetagheadersize = 32;
|
$apetagheadersize = 32;
|
||||||
$lyrics3tagsize = 10;
|
$lyrics3tagsize = 10;
|
||||||
|
|
||||||
if ($this->overrideendoffset == 0) {
|
if ($this->overrideendoffset == 0) {
|
||||||
$this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
|
|
||||||
$APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
|
|
||||||
|
|
||||||
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
|
$this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
|
||||||
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
|
$APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
|
||||||
// APE tag found before ID3v1
|
|
||||||
$info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
|
|
||||||
|
|
||||||
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
|
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
|
||||||
} elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
|
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
|
||||||
// APE tag found, no ID3v1
|
|
||||||
$info['ape']['tag_offset_end'] = $info['filesize'];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->fseek($this->overrideendoffset - $apetagheadersize);
|
|
||||||
if ($this->fread(8) == 'APETAGEX') {
|
|
||||||
$info['ape']['tag_offset_end'] = $this->overrideendoffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isset($info['ape']['tag_offset_end'])) {
|
|
||||||
// APE tag not found
|
|
||||||
unset($info['ape']);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shortcut
|
// APE tag found before ID3v1
|
||||||
$thisfile_ape = &$info['ape'];
|
$info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
|
||||||
|
|
||||||
$this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
|
//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
|
||||||
$APEfooterData = $this->fread(32);
|
} elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
|
||||||
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
|
|
||||||
$info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
// APE tag found, no ID3v1
|
||||||
$this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
|
$info['ape']['tag_offset_end'] = $info['filesize'];
|
||||||
$thisfile_ape['tag_offset_start'] = $this->ftell();
|
|
||||||
$APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
|
|
||||||
} else {
|
|
||||||
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
|
|
||||||
$this->fseek($thisfile_ape['tag_offset_start']);
|
|
||||||
$APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
|
|
||||||
}
|
|
||||||
$info['avdataend'] = $thisfile_ape['tag_offset_start'];
|
|
||||||
|
|
||||||
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
|
}
|
||||||
$info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
|
|
||||||
unset($info['id3v1']);
|
|
||||||
foreach ($info['warning'] as $key => $value) {
|
|
||||||
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
|
||||||
unset($info['warning'][$key]);
|
|
||||||
sort($info['warning']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$offset = 0;
|
} else {
|
||||||
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
|
||||||
if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
|
|
||||||
$offset += $apetagheadersize;
|
|
||||||
} else {
|
|
||||||
$info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shortcut
|
$this->fseek($this->overrideendoffset - $apetagheadersize);
|
||||||
$info['replay_gain'] = [];
|
if ($this->fread(8) == 'APETAGEX') {
|
||||||
$thisfile_replaygain = &$info['replay_gain'];
|
$info['ape']['tag_offset_end'] = $this->overrideendoffset;
|
||||||
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
|
}
|
||||||
$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
if (!isset($info['ape']['tag_offset_end'])) {
|
||||||
$offset += 4;
|
|
||||||
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
|
||||||
$offset += 4;
|
|
||||||
if (strstr(substr($APEtagData, $offset), "\x00") === false) {
|
|
||||||
$info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
|
|
||||||
$item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
|
|
||||||
|
|
||||||
// shortcut
|
// APE tag not found
|
||||||
$thisfile_ape['items'][$item_key] = [];
|
unset($info['ape']);
|
||||||
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
|
return false;
|
||||||
|
|
||||||
$thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
|
}
|
||||||
|
|
||||||
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
|
// shortcut
|
||||||
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
|
$thisfile_ape = &$info['ape'];
|
||||||
$offset += $value_size;
|
|
||||||
|
|
||||||
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
|
$this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
|
||||||
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
|
$APEfooterData = $this->fread(32);
|
||||||
case 0: // UTF-8
|
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
|
||||||
case 2: // Locator (URL, filename, etc), UTF-8 encoded
|
$this->error('Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']);
|
||||||
$thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
|
return false;
|
||||||
break;
|
}
|
||||||
|
|
||||||
case 1: // binary data
|
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
||||||
default:
|
$this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
|
||||||
break;
|
$thisfile_ape['tag_offset_start'] = $this->ftell();
|
||||||
}
|
$APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
|
||||||
|
} else {
|
||||||
|
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
|
||||||
|
$this->fseek($thisfile_ape['tag_offset_start']);
|
||||||
|
$APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
|
||||||
|
}
|
||||||
|
$info['avdataend'] = $thisfile_ape['tag_offset_start'];
|
||||||
|
|
||||||
switch (strtolower($item_key)) {
|
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
|
||||||
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
|
$this->warning('ID3v1 tag information ignored since it appears to be a false synch in APEtag data');
|
||||||
case 'replaygain_track_gain':
|
unset($info['id3v1']);
|
||||||
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
foreach ($info['warning'] as $key => $value) {
|
||||||
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
||||||
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
unset($info['warning'][$key]);
|
||||||
} else {
|
sort($info['warning']);
|
||||||
$info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
break;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'replaygain_track_peak':
|
$offset = 0;
|
||||||
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
|
||||||
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
|
||||||
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
$offset += $apetagheadersize;
|
||||||
if ($thisfile_replaygain['track']['peak'] <= 0) {
|
} else {
|
||||||
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
$this->error('Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']);
|
||||||
}
|
return false;
|
||||||
} else {
|
}
|
||||||
$info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'replaygain_album_gain':
|
// shortcut
|
||||||
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
$info['replay_gain'] = array();
|
||||||
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
$thisfile_replaygain = &$info['replay_gain'];
|
||||||
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'replaygain_album_peak':
|
for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
|
||||||
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
||||||
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
$offset += 4;
|
||||||
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
|
||||||
if ($thisfile_replaygain['album']['peak'] <= 0) {
|
$offset += 4;
|
||||||
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
|
if (strstr(substr($APEtagData, $offset), "\x00") === false) {
|
||||||
}
|
$this->error('Cannot find null-byte (0x00) separator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset));
|
||||||
} else {
|
return false;
|
||||||
$info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
}
|
||||||
}
|
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
|
||||||
break;
|
$item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
|
||||||
|
|
||||||
case 'mp3gain_undo':
|
// shortcut
|
||||||
if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
|
$thisfile_ape['items'][$item_key] = array();
|
||||||
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
|
$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
|
||||||
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
|
|
||||||
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
|
|
||||||
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mp3gain_minmax':
|
$thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
|
||||||
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
|
|
||||||
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
|
||||||
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
|
|
||||||
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mp3gain_album_minmax':
|
$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
|
||||||
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
|
$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
|
||||||
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
$offset += $value_size;
|
||||||
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
|
|
||||||
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'tracknumber':
|
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
|
||||||
if (is_array($thisfile_ape_items_current['data'])) {
|
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
|
||||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
case 0: // UTF-8
|
||||||
$thisfile_ape['comments']['track'][] = $comment;
|
case 2: // Locator (URL, filename, etc), UTF-8 encoded
|
||||||
}
|
$thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cover art (artist)':
|
case 1: // binary data
|
||||||
case 'cover art (back)':
|
default:
|
||||||
case 'cover art (band logo)':
|
break;
|
||||||
case 'cover art (band)':
|
}
|
||||||
case 'cover art (colored fish)':
|
|
||||||
case 'cover art (composer)':
|
|
||||||
case 'cover art (conductor)':
|
|
||||||
case 'cover art (front)':
|
|
||||||
case 'cover art (icon)':
|
|
||||||
case 'cover art (illustration)':
|
|
||||||
case 'cover art (lead)':
|
|
||||||
case 'cover art (leaflet)':
|
|
||||||
case 'cover art (lyricist)':
|
|
||||||
case 'cover art (media)':
|
|
||||||
case 'cover art (movie scene)':
|
|
||||||
case 'cover art (other icon)':
|
|
||||||
case 'cover art (other)':
|
|
||||||
case 'cover art (performance)':
|
|
||||||
case 'cover art (publisher logo)':
|
|
||||||
case 'cover art (recording)':
|
|
||||||
case 'cover art (studio)':
|
|
||||||
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
|
|
||||||
if (is_array($thisfile_ape_items_current['data'])) {
|
|
||||||
$info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
|
|
||||||
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
|
|
||||||
}
|
|
||||||
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
|
|
||||||
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
|
|
||||||
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
|
|
||||||
|
|
||||||
do {
|
switch (strtolower($item_key)) {
|
||||||
$thisfile_ape_items_current['image_mime'] = '';
|
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
|
||||||
$imageinfo = [];
|
case 'replaygain_track_gain':
|
||||||
$imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
|
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) {
|
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||||
$info['warning'][] = 'APEtag "'.$item_key.'" contains invalid image data';
|
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
||||||
break;
|
} else {
|
||||||
}
|
$this->warning('MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
$thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if ($this->inline_attachments === false) {
|
case 'replaygain_track_peak':
|
||||||
// skip entirely
|
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
unset($thisfile_ape_items_current['data']);
|
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||||
break;
|
$thisfile_replaygain['track']['originator'] = 'unspecified';
|
||||||
}
|
if ($thisfile_replaygain['track']['peak'] <= 0) {
|
||||||
if ($this->inline_attachments === true) {
|
$this->warning('ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
|
||||||
// great
|
}
|
||||||
} elseif (is_int($this->inline_attachments)) {
|
} else {
|
||||||
if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
|
$this->warning('MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
// too big, skip
|
}
|
||||||
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)';
|
break;
|
||||||
unset($thisfile_ape_items_current['data']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} elseif (is_string($this->inline_attachments)) {
|
|
||||||
$this->inline_attachments = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
|
|
||||||
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
|
|
||||||
// cannot write, skip
|
|
||||||
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
|
|
||||||
unset($thisfile_ape_items_current['data']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we get this far, must be OK
|
|
||||||
if (is_string($this->inline_attachments)) {
|
|
||||||
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
|
|
||||||
if (!file_exists($destination_filename) || is_writable($destination_filename)) {
|
|
||||||
file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
|
|
||||||
}
|
|
||||||
$thisfile_ape_items_current['data_filename'] = $destination_filename;
|
|
||||||
unset($thisfile_ape_items_current['data']);
|
|
||||||
} else {
|
|
||||||
if (!isset($info['ape']['comments']['picture'])) {
|
|
||||||
$info['ape']['comments']['picture'] = [];
|
|
||||||
}
|
|
||||||
$comments_picture_data = [];
|
|
||||||
foreach (['data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength'] as $picture_key) {
|
|
||||||
if (isset($thisfile_ape_items_current[$picture_key])) {
|
|
||||||
$comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$info['ape']['comments']['picture'][] = $comments_picture_data;
|
|
||||||
unset($comments_picture_data);
|
|
||||||
}
|
|
||||||
} while (false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
case 'replaygain_album_gain':
|
||||||
if (is_array($thisfile_ape_items_current['data'])) {
|
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||||
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
||||||
}
|
} else {
|
||||||
}
|
$this->warning('MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
if (empty($thisfile_replaygain)) {
|
|
||||||
unset($info['replay_gain']);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parseAPEheaderFooter($APEheaderFooterData)
|
case 'replaygain_album_peak':
|
||||||
{
|
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
|
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
|
||||||
|
$thisfile_replaygain['album']['originator'] = 'unspecified';
|
||||||
|
if ($thisfile_replaygain['album']['peak'] <= 0) {
|
||||||
|
$this->warning('ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->warning('MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// shortcut
|
case 'mp3gain_undo':
|
||||||
$headerfooterinfo['raw'] = [];
|
if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
$headerfooterinfo_raw = &$headerfooterinfo['raw'];
|
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||||
|
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
|
||||||
|
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
|
||||||
|
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
|
||||||
|
} else {
|
||||||
|
$this->warning('MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
|
case 'mp3gain_minmax':
|
||||||
if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
|
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
return false;
|
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||||
}
|
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
|
||||||
$headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
|
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
|
||||||
$headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
|
} else {
|
||||||
$headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
|
$this->warning('MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
|
}
|
||||||
$headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
|
break;
|
||||||
|
|
||||||
$headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
|
case 'mp3gain_album_minmax':
|
||||||
if ($headerfooterinfo['tag_version'] >= 2) {
|
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
|
||||||
$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
|
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
|
||||||
}
|
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
|
||||||
return $headerfooterinfo;
|
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
|
||||||
}
|
} else {
|
||||||
|
$this->warning('MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
public function parseAPEtagFlags($rawflagint)
|
case 'tracknumber':
|
||||||
{
|
if (is_array($thisfile_ape_items_current['data'])) {
|
||||||
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
|
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||||
// All are set to zero on creation and ignored on reading."
|
$thisfile_ape['comments']['track'][] = $comment;
|
||||||
// http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
|
}
|
||||||
$flags['header'] = (bool) ($rawflagint & 0x80000000);
|
}
|
||||||
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
|
break;
|
||||||
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
|
|
||||||
$flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
|
|
||||||
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
|
|
||||||
|
|
||||||
$flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
|
case 'cover art (artist)':
|
||||||
|
case 'cover art (back)':
|
||||||
|
case 'cover art (band logo)':
|
||||||
|
case 'cover art (band)':
|
||||||
|
case 'cover art (colored fish)':
|
||||||
|
case 'cover art (composer)':
|
||||||
|
case 'cover art (conductor)':
|
||||||
|
case 'cover art (front)':
|
||||||
|
case 'cover art (icon)':
|
||||||
|
case 'cover art (illustration)':
|
||||||
|
case 'cover art (lead)':
|
||||||
|
case 'cover art (leaflet)':
|
||||||
|
case 'cover art (lyricist)':
|
||||||
|
case 'cover art (media)':
|
||||||
|
case 'cover art (movie scene)':
|
||||||
|
case 'cover art (other icon)':
|
||||||
|
case 'cover art (other)':
|
||||||
|
case 'cover art (performance)':
|
||||||
|
case 'cover art (publisher logo)':
|
||||||
|
case 'cover art (recording)':
|
||||||
|
case 'cover art (studio)':
|
||||||
|
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
|
||||||
|
if (is_array($thisfile_ape_items_current['data'])) {
|
||||||
|
$this->warning('APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8');
|
||||||
|
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
|
||||||
|
}
|
||||||
|
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
|
||||||
|
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
|
||||||
|
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
|
||||||
|
|
||||||
return $flags;
|
do {
|
||||||
}
|
$thisfile_ape_items_current['image_mime'] = '';
|
||||||
|
$imageinfo = array();
|
||||||
|
$imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
|
||||||
|
if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) {
|
||||||
|
$this->warning('APEtag "'.$item_key.'" contains invalid image data');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
|
||||||
|
|
||||||
public function APEcontentTypeFlagLookup($contenttypeid)
|
if ($this->inline_attachments === false) {
|
||||||
{
|
// skip entirely
|
||||||
static $APEcontentTypeFlagLookup = [
|
unset($thisfile_ape_items_current['data']);
|
||||||
0 => 'utf-8',
|
break;
|
||||||
1 => 'binary',
|
}
|
||||||
2 => 'external',
|
if ($this->inline_attachments === true) {
|
||||||
3 => 'reserved'
|
// great
|
||||||
];
|
} elseif (is_int($this->inline_attachments)) {
|
||||||
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
|
if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
|
||||||
}
|
// too big, skip
|
||||||
|
$this->warning('attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)');
|
||||||
|
unset($thisfile_ape_items_current['data']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} elseif (is_string($this->inline_attachments)) {
|
||||||
|
$this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
|
||||||
|
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
|
||||||
|
// cannot write, skip
|
||||||
|
$this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)');
|
||||||
|
unset($thisfile_ape_items_current['data']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we get this far, must be OK
|
||||||
|
if (is_string($this->inline_attachments)) {
|
||||||
|
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
|
||||||
|
if (!file_exists($destination_filename) || is_writable($destination_filename)) {
|
||||||
|
file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
|
||||||
|
} else {
|
||||||
|
$this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)');
|
||||||
|
}
|
||||||
|
$thisfile_ape_items_current['data_filename'] = $destination_filename;
|
||||||
|
unset($thisfile_ape_items_current['data']);
|
||||||
|
} else {
|
||||||
|
if (!isset($info['ape']['comments']['picture'])) {
|
||||||
|
$info['ape']['comments']['picture'] = array();
|
||||||
|
}
|
||||||
|
$comments_picture_data = array();
|
||||||
|
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
|
||||||
|
if (isset($thisfile_ape_items_current[$picture_key])) {
|
||||||
|
$comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$info['ape']['comments']['picture'][] = $comments_picture_data;
|
||||||
|
unset($comments_picture_data);
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (is_array($thisfile_ape_items_current['data'])) {
|
||||||
|
foreach ($thisfile_ape_items_current['data'] as $comment) {
|
||||||
|
$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (empty($thisfile_replaygain)) {
|
||||||
|
unset($info['replay_gain']);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseAPEheaderFooter($APEheaderFooterData) {
|
||||||
|
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
|
||||||
|
|
||||||
|
// shortcut
|
||||||
|
$headerfooterinfo['raw'] = array();
|
||||||
|
$headerfooterinfo_raw = &$headerfooterinfo['raw'];
|
||||||
|
|
||||||
|
$headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
|
||||||
|
if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
|
||||||
|
$headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
|
||||||
|
$headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
|
||||||
|
$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
|
||||||
|
$headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
|
||||||
|
|
||||||
|
$headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
|
||||||
|
if ($headerfooterinfo['tag_version'] >= 2) {
|
||||||
|
$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
|
||||||
|
}
|
||||||
|
return $headerfooterinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseAPEtagFlags($rawflagint) {
|
||||||
|
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
|
||||||
|
// All are set to zero on creation and ignored on reading."
|
||||||
|
// http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
|
||||||
|
$flags['header'] = (bool) ($rawflagint & 0x80000000);
|
||||||
|
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
|
||||||
|
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
|
||||||
|
$flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
|
||||||
|
$flags['read_only'] = (bool) ($rawflagint & 0x00000001);
|
||||||
|
|
||||||
|
$flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
|
||||||
|
|
||||||
|
return $flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function APEcontentTypeFlagLookup($contenttypeid) {
|
||||||
|
static $APEcontentTypeFlagLookup = array(
|
||||||
|
0 => 'utf-8',
|
||||||
|
1 => 'binary',
|
||||||
|
2 => 'external',
|
||||||
|
3 => 'reserved'
|
||||||
|
);
|
||||||
|
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function APEtagItemIsUTF8Lookup($itemkey) {
|
||||||
|
static $APEtagItemIsUTF8Lookup = array(
|
||||||
|
'title',
|
||||||
|
'subtitle',
|
||||||
|
'artist',
|
||||||
|
'album',
|
||||||
|
'debut album',
|
||||||
|
'publisher',
|
||||||
|
'conductor',
|
||||||
|
'track',
|
||||||
|
'composer',
|
||||||
|
'comment',
|
||||||
|
'copyright',
|
||||||
|
'publicationright',
|
||||||
|
'file',
|
||||||
|
'year',
|
||||||
|
'record date',
|
||||||
|
'record location',
|
||||||
|
'genre',
|
||||||
|
'media',
|
||||||
|
'related',
|
||||||
|
'isrc',
|
||||||
|
'abstract',
|
||||||
|
'language',
|
||||||
|
'bibliography'
|
||||||
|
);
|
||||||
|
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
|
||||||
|
}
|
||||||
|
|
||||||
public function APEtagItemIsUTF8Lookup($itemkey)
|
|
||||||
{
|
|
||||||
static $APEtagItemIsUTF8Lookup = [
|
|
||||||
'title',
|
|
||||||
'subtitle',
|
|
||||||
'artist',
|
|
||||||
'album',
|
|
||||||
'debut album',
|
|
||||||
'publisher',
|
|
||||||
'conductor',
|
|
||||||
'track',
|
|
||||||
'composer',
|
|
||||||
'comment',
|
|
||||||
'copyright',
|
|
||||||
'publicationright',
|
|
||||||
'file',
|
|
||||||
'year',
|
|
||||||
'record date',
|
|
||||||
'record location',
|
|
||||||
'genre',
|
|
||||||
'media',
|
|
||||||
'related',
|
|
||||||
'isrc',
|
|
||||||
'abstract',
|
|
||||||
'language',
|
|
||||||
'bibliography'
|
|
||||||
];
|
|
||||||
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,349 +18,364 @@
|
||||||
class getid3_id3v1 extends getid3_handler
|
class getid3_id3v1 extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
||||||
$info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
$this->warning('Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fseek(-256, SEEK_END);
|
$this->fseek(-256, SEEK_END);
|
||||||
$preid3v1 = $this->fread(128);
|
$preid3v1 = $this->fread(128);
|
||||||
$id3v1tag = $this->fread(128);
|
$id3v1tag = $this->fread(128);
|
||||||
|
|
||||||
if (substr($id3v1tag, 0, 3) == 'TAG') {
|
if (substr($id3v1tag, 0, 3) == 'TAG') {
|
||||||
$info['avdataend'] = $info['filesize'] - 128;
|
|
||||||
|
|
||||||
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
|
$info['avdataend'] = $info['filesize'] - 128;
|
||||||
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
|
|
||||||
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
|
|
||||||
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
|
|
||||||
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
|
|
||||||
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
|
|
||||||
|
|
||||||
// If second-last byte of comment field is null and last byte of comment field is non-null
|
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
|
||||||
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
|
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
|
||||||
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
|
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
|
||||||
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
|
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
|
||||||
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
|
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
|
||||||
}
|
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
|
||||||
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
|
|
||||||
|
|
||||||
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
|
// If second-last byte of comment field is null and last byte of comment field is non-null
|
||||||
if (!empty($ParsedID3v1['genre'])) {
|
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
|
||||||
unset($ParsedID3v1['genreid']);
|
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
|
||||||
}
|
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
|
||||||
if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) {
|
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
|
||||||
unset($ParsedID3v1['genre']);
|
}
|
||||||
}
|
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
|
||||||
|
|
||||||
foreach ($ParsedID3v1 as $key => $value) {
|
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
|
||||||
$ParsedID3v1['comments'][$key][0] = $value;
|
if (!empty($ParsedID3v1['genre'])) {
|
||||||
}
|
unset($ParsedID3v1['genreid']);
|
||||||
|
}
|
||||||
|
if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) {
|
||||||
|
unset($ParsedID3v1['genre']);
|
||||||
|
}
|
||||||
|
|
||||||
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
|
foreach ($ParsedID3v1 as $key => $value) {
|
||||||
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
|
$ParsedID3v1['comments'][$key][0] = $value;
|
||||||
$ParsedID3v1['title'],
|
}
|
||||||
$ParsedID3v1['artist'],
|
// ID3v1 encoding detection hack START
|
||||||
$ParsedID3v1['album'],
|
// ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
|
||||||
$ParsedID3v1['year'],
|
// Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
|
||||||
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
|
$ID3v1encoding = 'ISO-8859-1';
|
||||||
$ParsedID3v1['comment'],
|
foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
|
||||||
(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '')
|
foreach ($valuearray as $key => $value) {
|
||||||
);
|
if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
|
||||||
$ParsedID3v1['padding_valid'] = true;
|
foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
|
||||||
if ($id3v1tag !== $GoodFormatID3v1tag) {
|
if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
|
||||||
$ParsedID3v1['padding_valid'] = false;
|
$ID3v1encoding = $id3v1_bad_encoding;
|
||||||
$info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
|
break 3;
|
||||||
}
|
} elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
|
||||||
|
$ID3v1encoding = $id3v1_bad_encoding;
|
||||||
|
break 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ID3v1 encoding detection hack END
|
||||||
|
|
||||||
$ParsedID3v1['tag_offset_end'] = $info['filesize'];
|
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
|
||||||
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
|
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
|
||||||
|
$ParsedID3v1['title'],
|
||||||
|
$ParsedID3v1['artist'],
|
||||||
|
$ParsedID3v1['album'],
|
||||||
|
$ParsedID3v1['year'],
|
||||||
|
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
|
||||||
|
$ParsedID3v1['comment'],
|
||||||
|
(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
|
||||||
|
$ParsedID3v1['padding_valid'] = true;
|
||||||
|
if ($id3v1tag !== $GoodFormatID3v1tag) {
|
||||||
|
$ParsedID3v1['padding_valid'] = false;
|
||||||
|
$this->warning('Some ID3v1 fields do not use NULL characters for padding');
|
||||||
|
}
|
||||||
|
|
||||||
$info['id3v1'] = $ParsedID3v1;
|
$ParsedID3v1['tag_offset_end'] = $info['filesize'];
|
||||||
}
|
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
|
||||||
|
|
||||||
if (substr($preid3v1, 0, 3) == 'TAG') {
|
$info['id3v1'] = $ParsedID3v1;
|
||||||
// The way iTunes handles tags is, well, brain-damaged.
|
$info['id3v1']['encoding'] = $ID3v1encoding;
|
||||||
// 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
|
if (substr($preid3v1, 0, 3) == 'TAG') {
|
||||||
// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
|
// The way iTunes handles tags is, well, brain-damaged.
|
||||||
if (substr($preid3v1, 96, 8) == 'APETAGEX') {
|
// It completely ignores v1 if ID3v2 is present.
|
||||||
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch
|
// This goes as far as adding a new v1 tag *even if there already is one*
|
||||||
} 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
|
|
||||||
$info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
|
|
||||||
$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
|
||||||
|
$this->warning('Duplicate ID3v1 tag detected - this has been known to happen with iTunes');
|
||||||
|
$info['avdataend'] -= 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function cutfield($str)
|
return true;
|
||||||
{
|
}
|
||||||
return trim(substr($str, 0, strcspn($str, "\x00")));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ArrayOfGenres($allowSCMPXextended = false)
|
public static function cutfield($str) {
|
||||||
{
|
return trim(substr($str, 0, strcspn($str, "\x00")));
|
||||||
static $GenreLookup = [
|
}
|
||||||
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 => 'Thrash Metal',
|
|
||||||
145 => 'Anime',
|
|
||||||
146 => 'JPop',
|
|
||||||
147 => 'Synthpop',
|
|
||||||
|
|
||||||
255 => 'Unknown',
|
public 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 => 'Thrash Metal',
|
||||||
|
145 => 'Anime',
|
||||||
|
146 => 'JPop',
|
||||||
|
147 => 'Synthpop',
|
||||||
|
|
||||||
'CR' => 'Cover',
|
255 => 'Unknown',
|
||||||
'RX' => 'Remix'
|
|
||||||
];
|
|
||||||
|
|
||||||
static $GenreLookupSCMPX = [];
|
'CR' => 'Cover',
|
||||||
if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
|
'RX' => 'Remix'
|
||||||
$GenreLookupSCMPX = $GenreLookup;
|
);
|
||||||
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
|
|
||||||
// Extended ID3v1 genres invented by SCMPX
|
|
||||||
// Note that 255 "Japanese Anime" conflicts with standard "Unknown"
|
|
||||||
$GenreLookupSCMPX[240] = 'Sacred';
|
|
||||||
$GenreLookupSCMPX[241] = 'Northern Europe';
|
|
||||||
$GenreLookupSCMPX[242] = 'Irish & Scottish';
|
|
||||||
$GenreLookupSCMPX[243] = 'Scotland';
|
|
||||||
$GenreLookupSCMPX[244] = 'Ethnic Europe';
|
|
||||||
$GenreLookupSCMPX[245] = 'Enka';
|
|
||||||
$GenreLookupSCMPX[246] = 'Children\'s Song';
|
|
||||||
$GenreLookupSCMPX[247] = 'Japanese Sky';
|
|
||||||
$GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
|
|
||||||
$GenreLookupSCMPX[249] = 'Japanese Doom Rock';
|
|
||||||
$GenreLookupSCMPX[250] = 'Japanese J-POP';
|
|
||||||
$GenreLookupSCMPX[251] = 'Japanese Seiyu';
|
|
||||||
$GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
|
|
||||||
$GenreLookupSCMPX[253] = 'Japanese Moemoe';
|
|
||||||
$GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
|
|
||||||
//$GenreLookupSCMPX[255] = 'Japanese Anime';
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
|
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 LookupGenreName($genreid, $allowSCMPXextended = true)
|
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
|
||||||
{
|
}
|
||||||
switch ($genreid) {
|
|
||||||
case 'RX':
|
|
||||||
case 'CR':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!is_numeric($genreid)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$genreid = intval($genreid); // to handle 3 or '3' or '03'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
|
|
||||||
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function LookupGenreID($genre, $allowSCMPXextended = false)
|
public static function LookupGenreName($genreid, $allowSCMPXextended=true) {
|
||||||
{
|
switch ($genreid) {
|
||||||
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
|
case 'RX':
|
||||||
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
|
case 'CR':
|
||||||
foreach ($GenreLookup as $key => $value) {
|
break;
|
||||||
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
default:
|
||||||
return $key;
|
if (!is_numeric($genreid)) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
$genreid = intval($genreid); // to handle 3 or '3' or '03'
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
|
||||||
|
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
|
||||||
|
}
|
||||||
|
|
||||||
public static function StandardiseID3v1GenreName($OriginalGenre)
|
public static function LookupGenreID($genre, $allowSCMPXextended=false) {
|
||||||
{
|
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
|
||||||
if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
|
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
|
||||||
return self::LookupGenreName($GenreID);
|
foreach ($GenreLookup as $key => $value) {
|
||||||
}
|
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
|
||||||
return $OriginalGenre;
|
return $key;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track = '')
|
public static function StandardiseID3v1GenreName($OriginalGenre) {
|
||||||
{
|
if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
|
||||||
$ID3v1Tag = 'TAG';
|
return self::LookupGenreName($GenreID);
|
||||||
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
}
|
||||||
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
return $OriginalGenre;
|
||||||
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
}
|
||||||
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
|
|
||||||
if (!empty($track) && ($track > 0) && ($track <= 255)) {
|
public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
|
||||||
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
|
$ID3v1Tag = 'TAG';
|
||||||
$ID3v1Tag .= "\x00";
|
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||||
if (gettype($track) == 'string') {
|
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||||
$track = (int) $track;
|
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||||
}
|
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
|
||||||
$ID3v1Tag .= chr($track);
|
if (!empty($track) && ($track > 0) && ($track <= 255)) {
|
||||||
} else {
|
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
|
||||||
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
$ID3v1Tag .= "\x00";
|
||||||
}
|
if (gettype($track) == 'string') {
|
||||||
if (($genreid < 0) || ($genreid > 147)) {
|
$track = (int) $track;
|
||||||
$genreid = 255; // 'unknown' genre
|
}
|
||||||
}
|
$ID3v1Tag .= chr($track);
|
||||||
switch (gettype($genreid)) {
|
} else {
|
||||||
case 'string':
|
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
|
||||||
case 'integer':
|
}
|
||||||
$ID3v1Tag .= chr(intval($genreid));
|
if (($genreid < 0) || ($genreid > 147)) {
|
||||||
break;
|
$genreid = 255; // 'unknown' genre
|
||||||
default:
|
}
|
||||||
$ID3v1Tag .= chr(255); // 'unknown' genre
|
switch (gettype($genreid)) {
|
||||||
break;
|
case 'string':
|
||||||
}
|
case 'integer':
|
||||||
|
$ID3v1Tag .= chr(intval($genreid));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ID3v1Tag .= chr(255); // 'unknown' genre
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ID3v1Tag;
|
||||||
|
}
|
||||||
|
|
||||||
return $ID3v1Tag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,268 +18,281 @@
|
||||||
class getid3_lyrics3 extends getid3_handler
|
class getid3_lyrics3 extends getid3_handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Analyze()
|
public function Analyze() {
|
||||||
{
|
$info = &$this->getid3->info;
|
||||||
$info = &$this->getid3->info;
|
|
||||||
|
|
||||||
// http://www.volweb.cz/str/tags.htm
|
// http://www.volweb.cz/str/tags.htm
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
if (!getid3_lib::intValueSupported($info['filesize'])) {
|
||||||
$info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
$this->warning('Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
|
$this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
|
||||||
$lyrics3_id3v1 = $this->fread(128 + 9 + 6);
|
$lyrics3_id3v1 = $this->fread(128 + 9 + 6);
|
||||||
$lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
|
$lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
|
||||||
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
|
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
|
||||||
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
|
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
|
||||||
|
|
||||||
if ($lyrics3end == 'LYRICSEND') {
|
if ($lyrics3end == 'LYRICSEND') {
|
||||||
// Lyrics3v1, ID3v1, no APE
|
// Lyrics3v1, ID3v1, no APE
|
||||||
|
|
||||||
$lyrics3size = 5100;
|
$lyrics3size = 5100;
|
||||||
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
||||||
$lyrics3version = 1;
|
$lyrics3version = 1;
|
||||||
} elseif ($lyrics3end == 'LYRICS200') {
|
|
||||||
// Lyrics3v2, ID3v1, no APE
|
|
||||||
|
|
||||||
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
} elseif ($lyrics3end == 'LYRICS200') {
|
||||||
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
|
// Lyrics3v2, ID3v1, no APE
|
||||||
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
|
||||||
$lyrics3version = 2;
|
|
||||||
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
|
|
||||||
// Lyrics3v1, no ID3v1, no APE
|
|
||||||
|
|
||||||
$lyrics3size = 5100;
|
// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||||
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
|
||||||
$lyrics3version = 1;
|
$lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
|
||||||
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
$lyrics3version = 2;
|
||||||
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
|
|
||||||
// Lyrics3v2, no ID3v1, no APE
|
|
||||||
|
|
||||||
$lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
|
||||||
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
// Lyrics3v1, no ID3v1, no APE
|
||||||
$lyrics3version = 2;
|
|
||||||
} else {
|
|
||||||
if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
|
|
||||||
$this->fseek($info['ape']['tag_offset_start'] - 15);
|
|
||||||
$lyrics3lsz = $this->fread(6);
|
|
||||||
$lyrics3end = $this->fread(9);
|
|
||||||
|
|
||||||
if ($lyrics3end == 'LYRICSEND') {
|
$lyrics3size = 5100;
|
||||||
// Lyrics3v1, APE, maybe ID3v1
|
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
||||||
|
$lyrics3version = 1;
|
||||||
|
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
||||||
|
|
||||||
$lyrics3size = 5100;
|
} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
|
||||||
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
|
||||||
$info['avdataend'] = $lyrics3offset;
|
|
||||||
$lyrics3version = 1;
|
|
||||||
$info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
|
||||||
} elseif ($lyrics3end == 'LYRICS200') {
|
|
||||||
// Lyrics3v2, APE, maybe ID3v1
|
|
||||||
|
|
||||||
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
// Lyrics3v2, no ID3v1, no APE
|
||||||
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
|
||||||
$lyrics3version = 2;
|
|
||||||
$info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($lyrics3offset)) {
|
$lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||||
$info['avdataend'] = $lyrics3offset;
|
$lyrics3offset = $info['filesize'] - $lyrics3size;
|
||||||
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
|
$lyrics3version = 2;
|
||||||
|
|
||||||
if (!isset($info['ape'])) {
|
} else {
|
||||||
if (isset($info['lyrics3']['tag_offset_start'])) {
|
|
||||||
$GETID3_ERRORARRAY = &$info['warning'];
|
|
||||||
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
|
|
||||||
$getid3_temp = new getID3();
|
|
||||||
$getid3_temp->openfile($this->getid3->filename);
|
|
||||||
$getid3_apetag = new getid3_apetag($getid3_temp);
|
|
||||||
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
|
|
||||||
$getid3_apetag->Analyze();
|
|
||||||
if (!empty($getid3_temp->info['ape'])) {
|
|
||||||
$info['ape'] = $getid3_temp->info['ape'];
|
|
||||||
}
|
|
||||||
if (!empty($getid3_temp->info['replay_gain'])) {
|
|
||||||
$info['replay_gain'] = $getid3_temp->info['replay_gain'];
|
|
||||||
}
|
|
||||||
unset($getid3_temp, $getid3_apetag);
|
|
||||||
} else {
|
|
||||||
$info['warning'][] = 'Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
|
||||||
}
|
|
||||||
|
|
||||||
public function getLyrics3Data($endoffset, $version, $length)
|
$this->fseek($info['ape']['tag_offset_start'] - 15);
|
||||||
{
|
$lyrics3lsz = $this->fread(6);
|
||||||
// http://www.volweb.cz/str/tags.htm
|
$lyrics3end = $this->fread(9);
|
||||||
|
|
||||||
$info = &$this->getid3->info;
|
if ($lyrics3end == 'LYRICSEND') {
|
||||||
|
// Lyrics3v1, APE, maybe ID3v1
|
||||||
|
|
||||||
if (!getid3_lib::intValueSupported($endoffset)) {
|
$lyrics3size = 5100;
|
||||||
$info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
||||||
return false;
|
$info['avdataend'] = $lyrics3offset;
|
||||||
}
|
$lyrics3version = 1;
|
||||||
|
$this->warning('APE tag located after Lyrics3, will probably break Lyrics3 compatability');
|
||||||
|
|
||||||
$this->fseek($endoffset);
|
} elseif ($lyrics3end == 'LYRICS200') {
|
||||||
if ($length <= 0) {
|
// Lyrics3v2, APE, maybe ID3v1
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$rawdata = $this->fread($length);
|
|
||||||
|
|
||||||
$ParsedLyrics3['raw']['lyrics3version'] = $version;
|
$lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
|
||||||
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
$lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
|
||||||
$ParsedLyrics3['tag_offset_start'] = $endoffset;
|
$lyrics3version = 2;
|
||||||
$ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
|
$this->warning('APE tag located after Lyrics3, will probably break Lyrics3 compatability');
|
||||||
|
|
||||||
if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
|
}
|
||||||
if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
|
|
||||||
$info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
|
|
||||||
$info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
|
|
||||||
$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
|
|
||||||
$length = strlen($rawdata);
|
|
||||||
$ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
|
|
||||||
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
|
||||||
} else {
|
|
||||||
$info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
$info['error'][] = '"LYRICSEND" expected at '.($this->ftell() - 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'])) {
|
if (isset($lyrics3offset)) {
|
||||||
$i = 0;
|
$info['avdataend'] = $lyrics3offset;
|
||||||
$flagnames = ['lyrics', 'timestamps', 'inhibitrandom'];
|
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
|
||||||
foreach ($flagnames as $flagname) {
|
|
||||||
if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
|
|
||||||
$ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fieldnametranslation = ['ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author'];
|
if (!isset($info['ape'])) {
|
||||||
foreach ($fieldnametranslation as $key => $value) {
|
if (isset($info['lyrics3']['tag_offset_start'])) {
|
||||||
if (isset($ParsedLyrics3['raw'][$key])) {
|
$GETID3_ERRORARRAY = &$info['warning'];
|
||||||
$ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
|
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
|
||||||
}
|
$getid3_temp = new getID3();
|
||||||
}
|
$getid3_temp->openfile($this->getid3->filename);
|
||||||
|
$getid3_apetag = new getid3_apetag($getid3_temp);
|
||||||
|
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
|
||||||
|
$getid3_apetag->Analyze();
|
||||||
|
if (!empty($getid3_temp->info['ape'])) {
|
||||||
|
$info['ape'] = $getid3_temp->info['ape'];
|
||||||
|
}
|
||||||
|
if (!empty($getid3_temp->info['replay_gain'])) {
|
||||||
|
$info['replay_gain'] = $getid3_temp->info['replay_gain'];
|
||||||
|
}
|
||||||
|
unset($getid3_temp, $getid3_apetag);
|
||||||
|
} else {
|
||||||
|
$this->warning('Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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'] = (isset($imagearray[0]) ? $imagearray[0] : '');
|
|
||||||
$ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : '');
|
|
||||||
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($ParsedLyrics3['raw']['LYR'])) {
|
|
||||||
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$info['error'][] = '"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
return true;
|
||||||
$info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
|
}
|
||||||
return false;
|
|
||||||
break;
|
public function getLyrics3Data($endoffset, $version, $length) {
|
||||||
}
|
// http://www.volweb.cz/str/tags.htm
|
||||||
|
|
||||||
|
$info = &$this->getid3->info;
|
||||||
|
|
||||||
|
if (!getid3_lib::intValueSupported($endoffset)) {
|
||||||
|
$this->warning('Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fseek($endoffset);
|
||||||
|
if ($length <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$rawdata = $this->fread($length);
|
||||||
|
|
||||||
|
$ParsedLyrics3['raw']['lyrics3version'] = $version;
|
||||||
|
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
||||||
|
$ParsedLyrics3['tag_offset_start'] = $endoffset;
|
||||||
|
$ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
|
||||||
|
|
||||||
|
if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
|
||||||
|
if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
|
||||||
|
|
||||||
|
$this->warning('"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version);
|
||||||
|
$info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
|
||||||
|
$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
|
||||||
|
$length = strlen($rawdata);
|
||||||
|
$ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
|
||||||
|
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->error('"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead');
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
$this->error('"LYRICSEND" expected at '.($this->ftell() - 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'] = (isset($imagearray[0]) ? $imagearray[0] : '');
|
||||||
|
$ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : '');
|
||||||
|
$ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($ParsedLyrics3['raw']['LYR'])) {
|
||||||
|
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->error('"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->error('Cannot process Lyrics3 version '.$version.' (only v1 and v2)');
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) {
|
if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) {
|
||||||
$info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
|
$this->warning('ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data');
|
||||||
unset($info['id3v1']);
|
unset($info['id3v1']);
|
||||||
foreach ($info['warning'] as $key => $value) {
|
foreach ($info['warning'] as $key => $value) {
|
||||||
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
|
||||||
unset($info['warning'][$key]);
|
unset($info['warning'][$key]);
|
||||||
sort($info['warning']);
|
sort($info['warning']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['lyrics3'] = $ParsedLyrics3;
|
$info['lyrics3'] = $ParsedLyrics3;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Lyrics3Timestamp2Seconds($rawtimestamp)
|
public function Lyrics3Timestamp2Seconds($rawtimestamp) {
|
||||||
{
|
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
|
||||||
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
|
return (int) (($regs[1] * 60) + $regs[2]);
|
||||||
return (int) (($regs[1] * 60) + $regs[2]);
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function Lyrics3LyricsTimestampParse(&$Lyrics3data)
|
public function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
|
||||||
{
|
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
|
||||||
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
|
foreach ($lyricsarray as $key => $lyricline) {
|
||||||
foreach ($lyricsarray as $key => $lyricline) {
|
$regs = array();
|
||||||
$regs = [];
|
unset($thislinetimestamps);
|
||||||
unset($thislinetimestamps);
|
while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) {
|
||||||
while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) {
|
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
|
||||||
$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
|
$lyricline = str_replace($regs[0], '', $lyricline);
|
||||||
$lyricline = str_replace($regs[0], '', $lyricline);
|
}
|
||||||
}
|
$notimestamplyricsarray[$key] = $lyricline;
|
||||||
$notimestamplyricsarray[$key] = $lyricline;
|
if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
|
||||||
if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
|
sort($thislinetimestamps);
|
||||||
sort($thislinetimestamps);
|
foreach ($thislinetimestamps as $timestampkey => $timestamp) {
|
||||||
foreach ($thislinetimestamps as $timestampkey => $timestamp) {
|
if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
|
||||||
if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
|
// timestamps only have a 1-second resolution, it's possible that multiple lines
|
||||||
// timestamps only have a 1-second resolution, it's possible that multiple lines
|
// could have the same timestamp, if so, append
|
||||||
// could have the same timestamp, if so, append
|
$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
|
||||||
$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
|
} else {
|
||||||
} else {
|
$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
|
||||||
$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
|
||||||
$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
|
if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
|
||||||
if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
|
ksort($Lyrics3data['synchedlyrics']);
|
||||||
ksort($Lyrics3data['synchedlyrics']);
|
}
|
||||||
}
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function IntString2Bool($char)
|
public function IntString2Bool($char) {
|
||||||
{
|
if ($char == '1') {
|
||||||
if ($char == '1') {
|
return true;
|
||||||
return true;
|
} elseif ($char == '0') {
|
||||||
} elseif ($char == '0') {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,356 +33,388 @@
|
||||||
*************************************************************************************************/
|
*************************************************************************************************/
|
||||||
class Image_XMP
|
class Image_XMP
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* The name of the image file that contains the XMP fields to extract and modify.
|
* The name of the image file that contains the XMP fields to extract and modify.
|
||||||
* @see Image_XMP()
|
* @see Image_XMP()
|
||||||
*/
|
*/
|
||||||
public $_sFilename = null;
|
public $_sFilename = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
* The XMP fields that were extracted from the image or updated by this class.
|
* The XMP fields that were extracted from the image or updated by this class.
|
||||||
* @see getAllTags()
|
* @see getAllTags()
|
||||||
*/
|
*/
|
||||||
public $_aXMP = [];
|
public $_aXMP = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean
|
* @var boolean
|
||||||
* True if an APP1 segment was found to contain XMP metadata.
|
* True if an APP1 segment was found to contain XMP metadata.
|
||||||
* @see isValid()
|
* @see isValid()
|
||||||
*/
|
*/
|
||||||
public $_bXMPParse = false;
|
public $_bXMPParse = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of XMP parsing during instantiation
|
* Returns the status of XMP parsing during instantiation
|
||||||
*
|
*
|
||||||
* You'll normally want to call this method before trying to get XMP fields.
|
* You'll normally want to call this method before trying to get XMP fields.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
* Returns true if an APP1 segment was found to contain XMP metadata.
|
* Returns true if an APP1 segment was found to contain XMP metadata.
|
||||||
*/
|
*/
|
||||||
public function isValid()
|
public function isValid()
|
||||||
{
|
{
|
||||||
return $this->_bXMPParse;
|
return $this->_bXMPParse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a copy of all XMP tags extracted from the image
|
* 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
|
* @return array - An array of XMP fields as it extracted by the XMPparse() function
|
||||||
*/
|
*/
|
||||||
public function getAllTags()
|
public function getAllTags()
|
||||||
{
|
{
|
||||||
return $this->_aXMP;
|
return $this->_aXMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads all the JPEG header segments from an JPEG image file into an array
|
* 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
|
* @param string $filename - the filename of the JPEG file to read
|
||||||
* @return array $headerdata - Array of JPEG header segments
|
* @return array $headerdata - Array of JPEG header segments
|
||||||
* @return boolean FALSE - if headers could not be read
|
* @return boolean FALSE - if headers could not be read
|
||||||
*/
|
*/
|
||||||
public function _get_jpeg_header_data($filename)
|
public function _get_jpeg_header_data($filename)
|
||||||
{
|
{
|
||||||
// prevent refresh from aborting file operations and hosing file
|
// prevent refresh from aborting file operations and hosing file
|
||||||
ignore_user_abort(true);
|
ignore_user_abort(true);
|
||||||
|
|
||||||
// Attempt to open the jpeg file - the at symbol supresses the error message about
|
// 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
|
// 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.
|
// does not work with files fetched over http or ftp.
|
||||||
if (is_readable($filename) && is_file($filename) && ($filehnd = fopen($filename, 'rb'))) {
|
if (is_readable($filename) && is_file($filename) && ($filehnd = fopen($filename, 'rb'))) {
|
||||||
// great
|
// great
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the first two characters
|
// Read the first two characters
|
||||||
$data = fread($filehnd, 2);
|
$data = fread($filehnd, 2);
|
||||||
|
|
||||||
// Check that the first two characters are 0xFF 0xD8 (SOI - Start of image)
|
// Check that the first two characters are 0xFF 0xD8 (SOI - Start of image)
|
||||||
if ($data != "\xFF\xD8") {
|
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";
|
// No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
|
||||||
fclose($filehnd);
|
echo '<p>This probably is not a JPEG file</p>'."\n";
|
||||||
return false;
|
fclose($filehnd);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Read the third character
|
// Read the third character
|
||||||
$data = fread($filehnd, 2);
|
$data = fread($filehnd, 2);
|
||||||
|
|
||||||
// Check that the third character is 0xFF (Start of first segment header)
|
// Check that the third character is 0xFF (Start of first segment header)
|
||||||
if ($data{0} != "\xFF") {
|
if ($data{0} != "\xFF")
|
||||||
// NO FF found - close file and return - JPEG is probably corrupted
|
{
|
||||||
fclose($filehnd);
|
// NO FF found - close file and return - JPEG is probably corrupted
|
||||||
return false;
|
fclose($filehnd);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Flag that we havent yet hit the compressed image data
|
// Flag that we havent yet hit the compressed image data
|
||||||
$hit_compressed_image_data = false;
|
$hit_compressed_image_data = false;
|
||||||
|
|
||||||
// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
|
// 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)
|
// 2) we have hit the compressed image data (no more headers are allowed after data)
|
||||||
// 3) or end of file is hit
|
// 3) or end of file is hit
|
||||||
|
|
||||||
while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd))) {
|
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
|
// Found a segment to look at.
|
||||||
if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7)) {
|
// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
|
||||||
// Segment isn't a Restart marker
|
if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
|
||||||
// Read the next two bytes (size)
|
{
|
||||||
$sizestr = fread($filehnd, 2);
|
// Segment isn't a Restart marker
|
||||||
|
// Read the next two bytes (size)
|
||||||
|
$sizestr = fread($filehnd, 2);
|
||||||
|
|
||||||
// convert the size bytes to an integer
|
// convert the size bytes to an integer
|
||||||
$decodedsize = unpack('nsize', $sizestr);
|
$decodedsize = unpack('nsize', $sizestr);
|
||||||
|
|
||||||
// Save the start position of the data
|
// Save the start position of the data
|
||||||
$segdatastart = ftell($filehnd);
|
$segdatastart = ftell($filehnd);
|
||||||
|
|
||||||
// Read the segment data with length indicated by the previously read size
|
// Read the segment data with length indicated by the previously read size
|
||||||
$segdata = fread($filehnd, $decodedsize['size'] - 2);
|
$segdata = fread($filehnd, $decodedsize['size'] - 2);
|
||||||
|
|
||||||
// Store the segment information in the output array
|
// Store the segment information in the output array
|
||||||
$headerdata[] = [
|
$headerdata[] = array(
|
||||||
'SegType' => ord($data{1}),
|
'SegType' => ord($data{1}),
|
||||||
'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
|
'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
|
||||||
'SegDataStart' => $segdatastart,
|
'SegDataStart' => $segdatastart,
|
||||||
'SegData' => $segdata,
|
'SegData' => $segdata,
|
||||||
];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
|
// 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") {
|
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;
|
// Flag that we have hit the compressed image data - exit loop as no more headers available.
|
||||||
} else {
|
$hit_compressed_image_data = true;
|
||||||
// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
|
}
|
||||||
$data = fread($filehnd, 2);
|
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
|
// Check that the first byte of the two is 0xFF as it should be for a marker
|
||||||
if ($data{0} != "\xFF") {
|
if ($data{0} != "\xFF")
|
||||||
// NO FF found - close file and return - JPEG is probably corrupted
|
{
|
||||||
fclose($filehnd);
|
// NO FF found - close file and return - JPEG is probably corrupted
|
||||||
return false;
|
fclose($filehnd);
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close File
|
// Close File
|
||||||
fclose($filehnd);
|
fclose($filehnd);
|
||||||
// Alow the user to abort from now on
|
// Alow the user to abort from now on
|
||||||
ignore_user_abort(false);
|
ignore_user_abort(false);
|
||||||
|
|
||||||
// Return the header data retrieved
|
// Return the header data retrieved
|
||||||
return $headerdata;
|
return $headerdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves XMP information from an APP1 JPEG segment and returns the raw XML text as a string.
|
* 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
|
* @param string $filename - the filename of the JPEG file to read
|
||||||
* @return string $xmp_data - the string of raw XML text
|
* @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
|
* @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured
|
||||||
*/
|
*/
|
||||||
public function _get_XMP_text($filename)
|
public function _get_XMP_text($filename)
|
||||||
{
|
{
|
||||||
//Get JPEG header data
|
//Get JPEG header data
|
||||||
$jpeg_header_data = $this->_get_jpeg_header_data($filename);
|
$jpeg_header_data = $this->_get_jpeg_header_data($filename);
|
||||||
|
|
||||||
//Cycle through the header segments
|
//Cycle through the header segments
|
||||||
for ($i = 0; $i < count($jpeg_header_data); $i++) {
|
for ($i = 0; $i < count($jpeg_header_data); $i++)
|
||||||
// If we find an APP1 header,
|
{
|
||||||
if (strcmp($jpeg_header_data[$i]['SegName'], 'APP1') == 0) {
|
// If we find an APP1 header,
|
||||||
// And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
|
if (strcmp($jpeg_header_data[$i]['SegName'], 'APP1') == 0)
|
||||||
if (strncmp($jpeg_header_data[$i]['SegData'], 'http://ns.adobe.com/xap/1.0/'."\x00", 29) == 0) {
|
{
|
||||||
// Found a XMP/RDF block
|
// And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
|
||||||
// Return the XMP text
|
if (strncmp($jpeg_header_data[$i]['SegData'], 'http://ns.adobe.com/xap/1.0/'."\x00", 29) == 0)
|
||||||
$xmp_data = substr($jpeg_header_data[$i]['SegData'], 29);
|
{
|
||||||
|
// Found a XMP/RDF block
|
||||||
|
// Return the XMP text
|
||||||
|
$xmp_data = substr($jpeg_header_data[$i]['SegData'], 29);
|
||||||
|
|
||||||
return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153)
|
return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a string containing XMP data (XML), and returns an array
|
* Parses a string containing XMP data (XML), and returns an array
|
||||||
* which contains all the XMP (XML) information.
|
* which contains all the XMP (XML) information.
|
||||||
*
|
*
|
||||||
* @param string $xml_text - a string containing the XMP data (XML) to be parsed
|
* @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 array $xmp_array - an array containing all xmp details retrieved.
|
||||||
* @return boolean FALSE - couldn't parse the XMP data
|
* @return boolean FALSE - couldn't parse the XMP data
|
||||||
*/
|
*/
|
||||||
public function read_XMP_array_from_text($xmltext)
|
public function read_XMP_array_from_text($xmltext)
|
||||||
{
|
{
|
||||||
// Check if there actually is any text to parse
|
// Check if there actually is any text to parse
|
||||||
if (trim($xmltext) == '') {
|
if (trim($xmltext) == '')
|
||||||
return false;
|
{
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Create an instance of a xml parser to parse the XML text
|
// Create an instance of a xml parser to parse the XML text
|
||||||
$xml_parser = xml_parser_create('UTF-8');
|
$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
|
// 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
|
// We would like to remove unneccessary white space, but this will also
|
||||||
// remove things like newlines (
) in the XML values, so white space
|
// remove things like newlines (
) in the XML values, so white space
|
||||||
// will have to be removed later
|
// will have to be removed later
|
||||||
if (xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 0) == false) {
|
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);
|
// Error setting case folding - destroy the parser and return
|
||||||
return false;
|
xml_parser_free($xml_parser);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// to use XML code correctly we have to turn case folding
|
// to use XML code correctly we have to turn case folding
|
||||||
// (uppercasing) off. XML is case sensitive and upper
|
// (uppercasing) off. XML is case sensitive and upper
|
||||||
// casing is in reality XML standards violation
|
// casing is in reality XML standards violation
|
||||||
if (xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0) == false) {
|
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);
|
// Error setting case folding - destroy the parser and return
|
||||||
return false;
|
xml_parser_free($xml_parser);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the XML text into a array structure
|
// Parse the XML text into a array structure
|
||||||
if (xml_parse_into_struct($xml_parser, $xmltext, $values, $tags) == 0) {
|
if (xml_parse_into_struct($xml_parser, $xmltext, $values, $tags) == 0)
|
||||||
// Error Parsing XML - destroy the parser and return
|
{
|
||||||
xml_parser_free($xml_parser);
|
// Error Parsing XML - destroy the parser and return
|
||||||
return false;
|
xml_parser_free($xml_parser);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy the xml parser
|
// Destroy the xml parser
|
||||||
xml_parser_free($xml_parser);
|
xml_parser_free($xml_parser);
|
||||||
|
|
||||||
// Clear the output array
|
// Clear the output array
|
||||||
$xmp_array = [];
|
$xmp_array = array();
|
||||||
|
|
||||||
// The XMP data has now been parsed into an array ...
|
// The XMP data has now been parsed into an array ...
|
||||||
|
|
||||||
// Cycle through each of the array elements
|
// Cycle through each of the array elements
|
||||||
$current_property = ''; // current property being processed
|
$current_property = ''; // current property being processed
|
||||||
$container_index = -1; // -1 = no container open, otherwise index of container content
|
$container_index = -1; // -1 = no container open, otherwise index of container content
|
||||||
foreach ($values as $xml_elem) {
|
foreach ($values as $xml_elem)
|
||||||
// Syntax and Class names
|
{
|
||||||
switch ($xml_elem['tag']) {
|
// Syntax and Class names
|
||||||
case 'x:xmpmeta':
|
switch ($xml_elem['tag'])
|
||||||
// only defined attribute is x:xmptk written by Adobe XMP Toolkit; value is the version of the toolkit
|
{
|
||||||
break;
|
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':
|
case 'rdf:RDF':
|
||||||
// required element immediately within x:xmpmeta; no data here
|
// required element immediately within x:xmpmeta; no data here
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'rdf:Description':
|
case 'rdf:Description':
|
||||||
switch ($xml_elem['type']) {
|
switch ($xml_elem['type'])
|
||||||
case 'open':
|
{
|
||||||
case 'complete':
|
case 'open':
|
||||||
if (array_key_exists('attributes', $xml_elem)) {
|
case 'complete':
|
||||||
// rdf:Description may contain wanted attributes
|
if (array_key_exists('attributes', $xml_elem))
|
||||||
foreach (array_keys($xml_elem['attributes']) as $key) {
|
{
|
||||||
// Check whether we want this details from this attribute
|
// 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']))
|
// if (in_array($key, $GLOBALS['XMP_tag_captions']))
|
||||||
if (true) {
|
if (true)
|
||||||
// Attribute wanted
|
{
|
||||||
$xmp_array[$key] = $xml_elem['attributes'][$key];
|
// Attribute wanted
|
||||||
}
|
$xmp_array[$key] = $xml_elem['attributes'][$key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'cdata':
|
}
|
||||||
case 'close':
|
case 'cdata':
|
||||||
break;
|
case 'close':
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'rdf:ID':
|
case 'rdf:ID':
|
||||||
case 'rdf:nodeID':
|
case 'rdf:nodeID':
|
||||||
// Attributes are ignored
|
// Attributes are ignored
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'rdf:li':
|
case 'rdf:li':
|
||||||
// Property member
|
// Property member
|
||||||
if ($xml_elem['type'] == 'complete') {
|
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 (array_key_exists('attributes', $xml_elem))
|
||||||
if (isset($xml_elem['attributes']['xml:lang']) && ($xml_elem['attributes']['xml:lang'] != 'x-default')) {
|
{
|
||||||
break;
|
// If Lang Alt (language alternatives) then ensure we take the default language
|
||||||
}
|
if (isset($xml_elem['attributes']['xml:lang']) && ($xml_elem['attributes']['xml:lang'] != 'x-default'))
|
||||||
}
|
{
|
||||||
if ($current_property != '') {
|
break;
|
||||||
$xmp_array[$current_property][$container_index] = (isset($xml_elem['value']) ? $xml_elem['value'] : '');
|
}
|
||||||
$container_index += 1;
|
}
|
||||||
}
|
if ($current_property != '')
|
||||||
//else unidentified attribute!!
|
{
|
||||||
}
|
$xmp_array[$current_property][$container_index] = (isset($xml_elem['value']) ? $xml_elem['value'] : '');
|
||||||
break;
|
$container_index += 1;
|
||||||
|
}
|
||||||
|
//else unidentified attribute!!
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'rdf:Seq':
|
case 'rdf:Seq':
|
||||||
case 'rdf:Bag':
|
case 'rdf:Bag':
|
||||||
case 'rdf:Alt':
|
case 'rdf:Alt':
|
||||||
// Container found
|
// Container found
|
||||||
switch ($xml_elem['type']) {
|
switch ($xml_elem['type'])
|
||||||
case 'open':
|
{
|
||||||
$container_index = 0;
|
case 'open':
|
||||||
break;
|
$container_index = 0;
|
||||||
case 'close':
|
break;
|
||||||
$container_index = -1;
|
case 'close':
|
||||||
break;
|
$container_index = -1;
|
||||||
case 'cdata':
|
break;
|
||||||
break;
|
case 'cdata':
|
||||||
}
|
break;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Check whether we want the details from this attribute
|
// Check whether we want the details from this attribute
|
||||||
// if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
|
// if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
|
||||||
if (true) {
|
if (true)
|
||||||
switch ($xml_elem['type']) {
|
{
|
||||||
case 'open':
|
switch ($xml_elem['type'])
|
||||||
// open current element
|
{
|
||||||
$current_property = $xml_elem['tag'];
|
case 'open':
|
||||||
break;
|
// open current element
|
||||||
|
$current_property = $xml_elem['tag'];
|
||||||
|
break;
|
||||||
|
|
||||||
case 'close':
|
case 'close':
|
||||||
// close current element
|
// close current element
|
||||||
$current_property = '';
|
$current_property = '';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'complete':
|
case 'complete':
|
||||||
// store attribute value
|
// store attribute value
|
||||||
$xmp_array[$xml_elem['tag']] = (isset($xml_elem['attributes']) ? $xml_elem['attributes'] : (isset($xml_elem['value']) ? $xml_elem['value'] : ''));
|
$xmp_array[$xml_elem['tag']] = (isset($xml_elem['attributes']) ? $xml_elem['attributes'] : (isset($xml_elem['value']) ? $xml_elem['value'] : ''));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'cdata':
|
case 'cdata':
|
||||||
// ignore
|
// ignore
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return $xmp_array;
|
}
|
||||||
}
|
return $xmp_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param string - Name of the image file to access and extract XMP information from.
|
* @param string - Name of the image file to access and extract XMP information from.
|
||||||
*/
|
*/
|
||||||
public function __construct($sFilename)
|
public function __construct($sFilename)
|
||||||
{
|
{
|
||||||
$this->_sFilename = $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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -667,70 +699,70 @@ $GLOBALS['XMP_tag_captions'] = array(
|
||||||
*
|
*
|
||||||
* The names of the JPEG segment markers, indexed by their marker number
|
* The names of the JPEG segment markers, indexed by their marker number
|
||||||
*/
|
*/
|
||||||
$GLOBALS['JPEG_Segment_Names'] = [
|
$GLOBALS['JPEG_Segment_Names'] = array(
|
||||||
0x01 => 'TEM',
|
0x01 => 'TEM',
|
||||||
0x02 => 'RES',
|
0x02 => 'RES',
|
||||||
0xC0 => 'SOF0',
|
0xC0 => 'SOF0',
|
||||||
0xC1 => 'SOF1',
|
0xC1 => 'SOF1',
|
||||||
0xC2 => 'SOF2',
|
0xC2 => 'SOF2',
|
||||||
0xC3 => 'SOF4',
|
0xC3 => 'SOF4',
|
||||||
0xC4 => 'DHT',
|
0xC4 => 'DHT',
|
||||||
0xC5 => 'SOF5',
|
0xC5 => 'SOF5',
|
||||||
0xC6 => 'SOF6',
|
0xC6 => 'SOF6',
|
||||||
0xC7 => 'SOF7',
|
0xC7 => 'SOF7',
|
||||||
0xC8 => 'JPG',
|
0xC8 => 'JPG',
|
||||||
0xC9 => 'SOF9',
|
0xC9 => 'SOF9',
|
||||||
0xCA => 'SOF10',
|
0xCA => 'SOF10',
|
||||||
0xCB => 'SOF11',
|
0xCB => 'SOF11',
|
||||||
0xCC => 'DAC',
|
0xCC => 'DAC',
|
||||||
0xCD => 'SOF13',
|
0xCD => 'SOF13',
|
||||||
0xCE => 'SOF14',
|
0xCE => 'SOF14',
|
||||||
0xCF => 'SOF15',
|
0xCF => 'SOF15',
|
||||||
0xD0 => 'RST0',
|
0xD0 => 'RST0',
|
||||||
0xD1 => 'RST1',
|
0xD1 => 'RST1',
|
||||||
0xD2 => 'RST2',
|
0xD2 => 'RST2',
|
||||||
0xD3 => 'RST3',
|
0xD3 => 'RST3',
|
||||||
0xD4 => 'RST4',
|
0xD4 => 'RST4',
|
||||||
0xD5 => 'RST5',
|
0xD5 => 'RST5',
|
||||||
0xD6 => 'RST6',
|
0xD6 => 'RST6',
|
||||||
0xD7 => 'RST7',
|
0xD7 => 'RST7',
|
||||||
0xD8 => 'SOI',
|
0xD8 => 'SOI',
|
||||||
0xD9 => 'EOI',
|
0xD9 => 'EOI',
|
||||||
0xDA => 'SOS',
|
0xDA => 'SOS',
|
||||||
0xDB => 'DQT',
|
0xDB => 'DQT',
|
||||||
0xDC => 'DNL',
|
0xDC => 'DNL',
|
||||||
0xDD => 'DRI',
|
0xDD => 'DRI',
|
||||||
0xDE => 'DHP',
|
0xDE => 'DHP',
|
||||||
0xDF => 'EXP',
|
0xDF => 'EXP',
|
||||||
0xE0 => 'APP0',
|
0xE0 => 'APP0',
|
||||||
0xE1 => 'APP1',
|
0xE1 => 'APP1',
|
||||||
0xE2 => 'APP2',
|
0xE2 => 'APP2',
|
||||||
0xE3 => 'APP3',
|
0xE3 => 'APP3',
|
||||||
0xE4 => 'APP4',
|
0xE4 => 'APP4',
|
||||||
0xE5 => 'APP5',
|
0xE5 => 'APP5',
|
||||||
0xE6 => 'APP6',
|
0xE6 => 'APP6',
|
||||||
0xE7 => 'APP7',
|
0xE7 => 'APP7',
|
||||||
0xE8 => 'APP8',
|
0xE8 => 'APP8',
|
||||||
0xE9 => 'APP9',
|
0xE9 => 'APP9',
|
||||||
0xEA => 'APP10',
|
0xEA => 'APP10',
|
||||||
0xEB => 'APP11',
|
0xEB => 'APP11',
|
||||||
0xEC => 'APP12',
|
0xEC => 'APP12',
|
||||||
0xED => 'APP13',
|
0xED => 'APP13',
|
||||||
0xEE => 'APP14',
|
0xEE => 'APP14',
|
||||||
0xEF => 'APP15',
|
0xEF => 'APP15',
|
||||||
0xF0 => 'JPG0',
|
0xF0 => 'JPG0',
|
||||||
0xF1 => 'JPG1',
|
0xF1 => 'JPG1',
|
||||||
0xF2 => 'JPG2',
|
0xF2 => 'JPG2',
|
||||||
0xF3 => 'JPG3',
|
0xF3 => 'JPG3',
|
||||||
0xF4 => 'JPG4',
|
0xF4 => 'JPG4',
|
||||||
0xF5 => 'JPG5',
|
0xF5 => 'JPG5',
|
||||||
0xF6 => 'JPG6',
|
0xF6 => 'JPG6',
|
||||||
0xF7 => 'JPG7',
|
0xF7 => 'JPG7',
|
||||||
0xF8 => 'JPG8',
|
0xF8 => 'JPG8',
|
||||||
0xF9 => 'JPG9',
|
0xF9 => 'JPG9',
|
||||||
0xFA => 'JPG10',
|
0xFA => 'JPG10',
|
||||||
0xFB => 'JPG11',
|
0xFB => 'JPG11',
|
||||||
0xFC => 'JPG12',
|
0xFC => 'JPG12',
|
||||||
0xFD => 'JPG13',
|
0xFD => 'JPG13',
|
||||||
0xFE => 'COM',
|
0xFE => 'COM',
|
||||||
];
|
);
|
||||||
|
|
|
@ -20,208 +20,205 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE
|
||||||
class getid3_write_apetag
|
class getid3_write_apetag
|
||||||
{
|
{
|
||||||
|
|
||||||
public $filename;
|
public $filename;
|
||||||
public $tag_data;
|
public $tag_data;
|
||||||
public $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
|
public $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteAPEtag()
|
public function WriteAPEtag() {
|
||||||
{
|
// NOTE: All data passed to this function must be UTF-8 format
|
||||||
// NOTE: All data passed to this function must be UTF-8 format
|
|
||||||
|
|
||||||
$getID3 = new getID3;
|
$getID3 = new getID3;
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
|
|
||||||
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
||||||
if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
|
if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
|
||||||
// Current APE tag between Lyrics3 and ID3v1/EOF
|
// Current APE tag between Lyrics3 and ID3v1/EOF
|
||||||
// This break Lyrics3 functionality
|
// This break Lyrics3 functionality
|
||||||
if (!$this->DeleteAPEtag()) {
|
if (!$this->DeleteAPEtag()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->always_preserve_replaygain) {
|
if ($this->always_preserve_replaygain) {
|
||||||
$ReplayGainTagsToPreserve = ['mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain'];
|
$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) {
|
foreach ($ReplayGainTagsToPreserve as $rg_key) {
|
||||||
if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
|
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];
|
$this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($APEtag = $this->GenerateAPEtag()) {
|
if ($APEtag = $this->GenerateAPEtag()) {
|
||||||
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
flock($fp, LOCK_EX);
|
flock($fp, LOCK_EX);
|
||||||
|
|
||||||
$PostAPEdataOffset = $ThisFileInfo['avdataend'];
|
$PostAPEdataOffset = $ThisFileInfo['avdataend'];
|
||||||
if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
||||||
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
|
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
|
||||||
}
|
}
|
||||||
if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
|
if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
|
||||||
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||||
}
|
}
|
||||||
fseek($fp, $PostAPEdataOffset);
|
fseek($fp, $PostAPEdataOffset);
|
||||||
$PostAPEdata = '';
|
$PostAPEdata = '';
|
||||||
if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
|
if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
|
||||||
$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
|
$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek($fp, $PostAPEdataOffset);
|
fseek($fp, $PostAPEdataOffset);
|
||||||
if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
|
if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
|
||||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
||||||
}
|
}
|
||||||
ftruncate($fp, ftell($fp));
|
ftruncate($fp, ftell($fp));
|
||||||
fwrite($fp, $APEtag, strlen($APEtag));
|
fwrite($fp, $APEtag, strlen($APEtag));
|
||||||
if (!empty($PostAPEdata)) {
|
if (!empty($PostAPEdata)) {
|
||||||
fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
|
fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
|
||||||
}
|
}
|
||||||
flock($fp, LOCK_UN);
|
flock($fp, LOCK_UN);
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
ignore_user_abort($oldignoreuserabort);
|
ignore_user_abort($oldignoreuserabort);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DeleteAPEtag()
|
public function DeleteAPEtag() {
|
||||||
{
|
$getID3 = new getID3;
|
||||||
$getID3 = new getID3;
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
||||||
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
|
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
||||||
if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
|
||||||
flock($fp, LOCK_EX);
|
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
|
||||||
|
|
||||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_end']);
|
flock($fp, LOCK_EX);
|
||||||
$DataAfterAPE = '';
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
|
|
||||||
$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
|
|
||||||
}
|
|
||||||
|
|
||||||
ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
fseek($fp, $ThisFileInfo['ape']['tag_offset_end']);
|
||||||
fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
$DataAfterAPE = '';
|
||||||
|
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
|
||||||
|
$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($DataAfterAPE)) {
|
ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
||||||
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
|
fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
|
||||||
}
|
|
||||||
|
|
||||||
flock($fp, LOCK_UN);
|
if (!empty($DataAfterAPE)) {
|
||||||
fclose($fp);
|
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
|
||||||
ignore_user_abort($oldignoreuserabort);
|
}
|
||||||
|
|
||||||
return true;
|
flock($fp, LOCK_UN);
|
||||||
}
|
fclose($fp);
|
||||||
return false;
|
ignore_user_abort($oldignoreuserabort);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function GenerateAPEtag()
|
public function GenerateAPEtag() {
|
||||||
{
|
// NOTE: All data passed to this function must be UTF-8 format
|
||||||
// NOTE: All data passed to this function must be UTF-8 format
|
|
||||||
|
|
||||||
$items = [];
|
$items = array();
|
||||||
if (!is_array($this->tag_data)) {
|
if (!is_array($this->tag_data)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($this->tag_data as $key => $arrayofvalues) {
|
foreach ($this->tag_data as $key => $arrayofvalues) {
|
||||||
if (!is_array($arrayofvalues)) {
|
if (!is_array($arrayofvalues)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$valuestring = '';
|
$valuestring = '';
|
||||||
foreach ($arrayofvalues as $value) {
|
foreach ($arrayofvalues as $value) {
|
||||||
$valuestring .= str_replace("\x00", '', $value)."\x00";
|
$valuestring .= str_replace("\x00", '', $value)."\x00";
|
||||||
}
|
}
|
||||||
$valuestring = rtrim($valuestring, "\x00");
|
$valuestring = rtrim($valuestring, "\x00");
|
||||||
|
|
||||||
// Length of the assigned value in bytes
|
// Length of the assigned value in bytes
|
||||||
$tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
|
$tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
|
||||||
|
|
||||||
//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
|
//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
|
||||||
$tagitem .= "\x00\x00\x00\x00";
|
$tagitem .= "\x00\x00\x00\x00";
|
||||||
|
|
||||||
$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
|
$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
|
||||||
$tagitem .= $valuestring;
|
$tagitem .= $valuestring;
|
||||||
|
|
||||||
$items[] = $tagitem;
|
$items[] = $tagitem;
|
||||||
}
|
|
||||||
|
|
||||||
return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function GenerateAPEtagHeaderFooter(&$items, $isheader = false)
|
return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
|
||||||
{
|
}
|
||||||
$tagdatalength = 0;
|
|
||||||
foreach ($items as $itemdata) {
|
|
||||||
$tagdatalength += strlen($itemdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
$APEheader = 'APETAGEX';
|
public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
|
||||||
$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
|
$tagdatalength = 0;
|
||||||
$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
|
foreach ($items as $itemdata) {
|
||||||
$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
|
$tagdatalength += strlen($itemdata);
|
||||||
$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
|
}
|
||||||
$APEheader .= str_repeat("\x00", 8);
|
|
||||||
|
|
||||||
return $APEheader;
|
$APEheader = 'APETAGEX';
|
||||||
}
|
$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
|
||||||
|
$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
|
||||||
|
$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
|
||||||
|
$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
|
||||||
|
$APEheader .= str_repeat("\x00", 8);
|
||||||
|
|
||||||
public function GenerateAPEtagFlags($header = true, $footer = true, $isheader = false, $encodingid = 0, $readonly = false)
|
return $APEheader;
|
||||||
{
|
}
|
||||||
$APEtagFlags = array_fill(0, 4, 0);
|
|
||||||
if ($header) {
|
|
||||||
$APEtagFlags[0] |= 0x80; // Tag contains a header
|
|
||||||
}
|
|
||||||
if (!$footer) {
|
|
||||||
$APEtagFlags[0] |= 0x40; // Tag contains no footer
|
|
||||||
}
|
|
||||||
if ($isheader) {
|
|
||||||
$APEtagFlags[0] |= 0x20; // This is the header, not the footer
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0: Item contains text information coded in UTF-8
|
public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
|
||||||
// 1: Item contains binary information °)
|
$APEtagFlags = array_fill(0, 4, 0);
|
||||||
// 2: Item is a locator of external stored information °°)
|
if ($header) {
|
||||||
// 3: reserved
|
$APEtagFlags[0] |= 0x80; // Tag contains a header
|
||||||
$APEtagFlags[3] |= ($encodingid << 1);
|
}
|
||||||
|
if (!$footer) {
|
||||||
|
$APEtagFlags[0] |= 0x40; // Tag contains no footer
|
||||||
|
}
|
||||||
|
if ($isheader) {
|
||||||
|
$APEtagFlags[0] |= 0x20; // This is the header, not the footer
|
||||||
|
}
|
||||||
|
|
||||||
if ($readonly) {
|
// 0: Item contains text information coded in UTF-8
|
||||||
$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
|
// 1: Item contains binary information °)
|
||||||
}
|
// 2: Item is a locator of external stored information °°)
|
||||||
|
// 3: reserved
|
||||||
|
$APEtagFlags[3] |= ($encodingid << 1);
|
||||||
|
|
||||||
return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
|
if ($readonly) {
|
||||||
}
|
$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
|
||||||
|
}
|
||||||
|
|
||||||
public function CleanAPEtagItemKey($itemkey)
|
return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
|
||||||
{
|
}
|
||||||
$itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
|
|
||||||
|
|
||||||
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
|
public function CleanAPEtagItemKey($itemkey) {
|
||||||
switch (strtoupper($itemkey)) {
|
$itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
|
||||||
case 'EAN/UPC':
|
|
||||||
case 'ISBN':
|
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
|
||||||
case 'LC':
|
switch (strtoupper($itemkey)) {
|
||||||
case 'ISRC':
|
case 'EAN/UPC':
|
||||||
$itemkey = strtoupper($itemkey);
|
case 'ISBN':
|
||||||
break;
|
case 'LC':
|
||||||
|
case 'ISRC':
|
||||||
|
$itemkey = strtoupper($itemkey);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$itemkey = ucwords($itemkey);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return $itemkey;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
$itemkey = ucwords($itemkey);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $itemkey;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,122 +18,120 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE_
|
||||||
|
|
||||||
class getid3_write_id3v1
|
class getid3_write_id3v1
|
||||||
{
|
{
|
||||||
public $filename;
|
public $filename;
|
||||||
public $filesize;
|
public $filesize;
|
||||||
public $tag_data;
|
public $tag_data;
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteID3v1()
|
public function WriteID3v1() {
|
||||||
{
|
// File MUST be writeable - CHMOD(646) at least
|
||||||
// File MUST be writeable - CHMOD(646) at least
|
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
|
||||||
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
|
$this->setRealFileSize();
|
||||||
$this->setRealFileSize();
|
if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
|
||||||
if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
|
$this->errors[] = 'Unable to WriteID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
||||||
$this->errors[] = 'Unable to WriteID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
if ($fp_source = fopen($this->filename, 'r+b')) {
|
||||||
if ($fp_source = fopen($this->filename, 'r+b')) {
|
fseek($fp_source, -128, SEEK_END);
|
||||||
fseek($fp_source, -128, SEEK_END);
|
if (fread($fp_source, 3) == 'TAG') {
|
||||||
if (fread($fp_source, 3) == 'TAG') {
|
fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
|
||||||
fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
|
} else {
|
||||||
} else {
|
fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
|
||||||
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'] : '')));
|
||||||
$this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
|
|
||||||
|
|
||||||
$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
|
$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
|
||||||
(isset($this->tag_data['title']) ? $this->tag_data['title'] : ''),
|
(isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''),
|
||||||
(isset($this->tag_data['artist']) ? $this->tag_data['artist'] : ''),
|
(isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''),
|
||||||
(isset($this->tag_data['album']) ? $this->tag_data['album'] : ''),
|
(isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''),
|
||||||
(isset($this->tag_data['year']) ? $this->tag_data['year'] : ''),
|
(isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''),
|
||||||
(isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
|
(isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
|
||||||
(isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
|
(isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
|
||||||
(isset($this->tag_data['track']) ? $this->tag_data['track'] : '')
|
(isset($this->tag_data['track'] ) ? $this->tag_data['track'] : ''));
|
||||||
);
|
fwrite($fp_source, $new_id3v1_tag_data, 128);
|
||||||
fwrite($fp_source, $new_id3v1_tag_data, 128);
|
fclose($fp_source);
|
||||||
fclose($fp_source);
|
return true;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->errors[] = 'File is not writeable: '.$this->filename;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function FixID3v1Padding()
|
} else {
|
||||||
{
|
$this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
|
||||||
// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
|
return false;
|
||||||
// This function rewrites the ID3v1 tag with correct padding
|
}
|
||||||
|
}
|
||||||
|
$this->errors[] = 'File is not writeable: '.$this->filename;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize getID3 engine
|
public function FixID3v1Padding() {
|
||||||
$getID3 = new getID3;
|
// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
|
||||||
$getID3->option_tag_id3v2 = false;
|
// This function rewrites the ID3v1 tag with correct padding
|
||||||
$getID3->option_tag_apetag = false;
|
|
||||||
$getID3->option_tags_html = false;
|
|
||||||
$getID3->option_extra_info = false;
|
|
||||||
$getID3->option_tag_id3v1 = true;
|
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
|
||||||
if (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 RemoveID3v1()
|
// Initialize getID3 engine
|
||||||
{
|
$getID3 = new getID3;
|
||||||
// File MUST be writeable - CHMOD(646) at least
|
$getID3->option_tag_id3v2 = false;
|
||||||
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
|
$getID3->option_tag_apetag = false;
|
||||||
$this->setRealFileSize();
|
$getID3->option_tags_html = false;
|
||||||
if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
|
$getID3->option_extra_info = false;
|
||||||
$this->errors[] = 'Unable to RemoveID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
$getID3->option_tag_id3v1 = true;
|
||||||
return false;
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
}
|
if (isset($ThisFileInfo['tags']['id3v1'])) {
|
||||||
if ($fp_source = fopen($this->filename, 'r+b')) {
|
foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
|
||||||
fseek($fp_source, -128, SEEK_END);
|
$id3v1data[$key] = implode(',', $value);
|
||||||
if (fread($fp_source, 3) == 'TAG') {
|
}
|
||||||
ftruncate($fp_source, $this->filesize - 128);
|
$this->tag_data = $id3v1data;
|
||||||
} else {
|
return $this->WriteID3v1();
|
||||||
// no ID3v1 tag to begin with - do nothing
|
}
|
||||||
}
|
return false;
|
||||||
fclose($fp_source);
|
}
|
||||||
return true;
|
|
||||||
} else {
|
public function RemoveID3v1() {
|
||||||
$this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
|
// File MUST be writeable - CHMOD(646) at least
|
||||||
}
|
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
|
||||||
} else {
|
$this->setRealFileSize();
|
||||||
$this->errors[] = $this->filename.' is not writeable';
|
if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
|
||||||
}
|
$this->errors[] = 'Unable to RemoveID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ($fp_source = fopen($this->filename, 'r+b')) {
|
||||||
|
|
||||||
|
fseek($fp_source, -128, SEEK_END);
|
||||||
|
if (fread($fp_source, 3) == 'TAG') {
|
||||||
|
ftruncate($fp_source, $this->filesize - 128);
|
||||||
|
} else {
|
||||||
|
// no ID3v1 tag to begin with - do nothing
|
||||||
|
}
|
||||||
|
fclose($fp_source);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->errors[] = $this->filename.' is not writeable';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRealFileSize() {
|
||||||
|
if (PHP_INT_MAX > 2147483647) {
|
||||||
|
$this->filesize = filesize($this->filename);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 32-bit PHP will not return correct values for filesize() if file is >=2GB
|
||||||
|
// but getID3->analyze() has workarounds to get actual filesize
|
||||||
|
$getID3 = new getID3;
|
||||||
|
$getID3->option_tag_id3v1 = false;
|
||||||
|
$getID3->option_tag_id3v2 = false;
|
||||||
|
$getID3->option_tag_apetag = false;
|
||||||
|
$getID3->option_tags_html = false;
|
||||||
|
$getID3->option_extra_info = false;
|
||||||
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
|
$this->filesize = $ThisFileInfo['filesize'];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function setRealFileSize()
|
|
||||||
{
|
|
||||||
if (PHP_INT_MAX > 2147483647) {
|
|
||||||
$this->filesize = filesize($this->filename);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 32-bit PHP will not return correct values for filesize() if file is >=2GB
|
|
||||||
// but getID3->analyze() has workarounds to get actual filesize
|
|
||||||
$getID3 = new getID3;
|
|
||||||
$getID3->option_tag_id3v1 = false;
|
|
||||||
$getID3->option_tag_id3v2 = false;
|
|
||||||
$getID3->option_tag_apetag = false;
|
|
||||||
$getID3->option_tags_html = false;
|
|
||||||
$getID3->option_extra_info = false;
|
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
|
||||||
$this->filesize = $ThisFileInfo['filesize'];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,56 +17,56 @@
|
||||||
|
|
||||||
class getid3_write_lyrics3
|
class getid3_write_lyrics3
|
||||||
{
|
{
|
||||||
public $filename;
|
public $filename;
|
||||||
public $tag_data;
|
public $tag_data;
|
||||||
//public $lyrics3_version = 2; // 1 or 2
|
//public $lyrics3_version = 2; // 1 or 2
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteLyrics3()
|
public function WriteLyrics3() {
|
||||||
{
|
$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
|
||||||
$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
public function DeleteLyrics3() {
|
||||||
public function DeleteLyrics3()
|
// Initialize getID3 engine
|
||||||
{
|
$getID3 = new getID3;
|
||||||
// Initialize getID3 engine
|
$ThisFileInfo = $getID3->analyze($this->filename);
|
||||||
$getID3 = new getID3;
|
if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
||||||
$ThisFileInfo = $getID3->analyze($this->filename);
|
if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
||||||
if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
|
|
||||||
if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
|
|
||||||
flock($fp, LOCK_EX);
|
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
|
||||||
|
|
||||||
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end']);
|
flock($fp, LOCK_EX);
|
||||||
$DataAfterLyrics3 = '';
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
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']);
|
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end']);
|
||||||
|
$DataAfterLyrics3 = '';
|
||||||
|
if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
|
||||||
|
$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($DataAfterLyrics3)) {
|
ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||||
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
|
||||||
fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
|
|
||||||
}
|
|
||||||
|
|
||||||
flock($fp, LOCK_UN);
|
if (!empty($DataAfterLyrics3)) {
|
||||||
fclose($fp);
|
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
|
||||||
ignore_user_abort($oldignoreuserabort);
|
fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
|
||||||
|
}
|
||||||
|
|
||||||
|
flock($fp, LOCK_UN);
|
||||||
|
fclose($fp);
|
||||||
|
ignore_user_abort($oldignoreuserabort);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->errors[] = 'Cannot fopen('.$this->filename.', "a+b")';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no Lyrics3 present
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$this->errors[] = 'Cannot fopen('.$this->filename.', "a+b")';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no Lyrics3 present
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,136 +18,145 @@
|
||||||
class getid3_write_metaflac
|
class getid3_write_metaflac
|
||||||
{
|
{
|
||||||
|
|
||||||
public $filename;
|
public $filename;
|
||||||
public $tag_data;
|
public $tag_data;
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteMetaFLAC()
|
public function WriteMetaFLAC() {
|
||||||
{
|
|
||||||
|
|
||||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
|
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create file with new comments
|
// Create file with new comments
|
||||||
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
||||||
if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) {
|
if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) {
|
||||||
foreach ($this->tag_data as $key => $value) {
|
foreach ($this->tag_data as $key => $value) {
|
||||||
foreach ($value as $commentdata) {
|
foreach ($value as $commentdata) {
|
||||||
fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n");
|
fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose($fpcomments);
|
fclose($fpcomments);
|
||||||
} else {
|
|
||||||
$this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$tempcommentsfilename.'", "wb")';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
} else {
|
||||||
if (GETID3_OS_ISWINDOWS) {
|
$this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$tempcommentsfilename.'", "wb")';
|
||||||
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
return false;
|
||||||
//$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
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
if (GETID3_OS_ISWINDOWS) {
|
||||||
clearstatcache();
|
|
||||||
$timestampbeforewriting = filemtime($this->filename);
|
|
||||||
|
|
||||||
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||||
$metaflacError = `$commandline`;
|
//$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
|
||||||
|
|
||||||
if (empty($metaflacError)) {
|
// On top of that, if error messages are not always captured properly under Windows
|
||||||
clearstatcache();
|
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
clearstatcache();
|
||||||
$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
$timestampbeforewriting = filemtime($this->filename);
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// It's simpler on *nix
|
|
||||||
$commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
|
||||||
$metaflacError = `$commandline`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove temporary comments file
|
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
||||||
unlink($tempcommentsfilename);
|
$metaflacError = `$commandline`;
|
||||||
ignore_user_abort($oldignoreuserabort);
|
|
||||||
|
|
||||||
if (!empty($metaflacError)) {
|
if (empty($metaflacError)) {
|
||||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
clearstatcache();
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
} else {
|
||||||
}
|
|
||||||
|
// It's simpler on *nix
|
||||||
|
$commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1';
|
||||||
|
$metaflacError = `$commandline`;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove temporary comments file
|
||||||
|
unlink($tempcommentsfilename);
|
||||||
|
ignore_user_abort($oldignoreuserabort);
|
||||||
|
|
||||||
|
if (!empty($metaflacError)) {
|
||||||
|
|
||||||
|
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function DeleteMetaFLAC()
|
public function DeleteMetaFLAC() {
|
||||||
{
|
|
||||||
|
|
||||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
|
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
if (GETID3_OS_ISWINDOWS) {
|
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';
|
if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
|
||||||
$metaflacError = `$commandline`;
|
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||||
|
clearstatcache();
|
||||||
|
$timestampbeforewriting = filemtime($this->filename);
|
||||||
|
|
||||||
if (empty($metaflacError)) {
|
$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||||
clearstatcache();
|
$metaflacError = `$commandline`;
|
||||||
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)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($metaflacError)) {
|
} else {
|
||||||
$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
|
|
||||||
return false;
|
// It's simpler on *nix
|
||||||
}
|
$commandline = 'metaflac --remove-all-tags "'.$this->filename.'" 2>&1';
|
||||||
return true;
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function CleanmetaflacName($originalcommentname)
|
public function CleanmetaflacName($originalcommentname) {
|
||||||
{
|
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||||
// 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
|
||||||
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
|
// 0x7A inclusive (a-z).
|
||||||
// 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)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
@ -16,253 +16,259 @@
|
||||||
|
|
||||||
class getid3_write_real
|
class getid3_write_real
|
||||||
{
|
{
|
||||||
public $filename;
|
public $filename;
|
||||||
public $tag_data = [];
|
public $tag_data = array();
|
||||||
public $fread_buffer_size = 32768; // read buffer size in bytes
|
public $fread_buffer_size = 32768; // read buffer size in bytes
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
public $paddedlength = 512; // minimum length of CONT tag in bytes
|
public $paddedlength = 512; // minimum length of CONT tag in bytes
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteReal()
|
public function WriteReal() {
|
||||||
{
|
// File MUST be writeable - CHMOD(646) at least
|
||||||
// File MUST be writeable - CHMOD(646) at least
|
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
||||||
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
|
||||||
// Initialize getID3 engine
|
|
||||||
$getID3 = new getID3;
|
|
||||||
$OldThisFileInfo = $getID3->analyze($this->filename);
|
|
||||||
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
|
||||||
$this->errors[] = 'Cannot write Real tags on old-style file format';
|
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($OldThisFileInfo['real']['chunks'])) {
|
// Initialize getID3 engine
|
||||||
$this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
|
$getID3 = new getID3;
|
||||||
fclose($fp_source);
|
$OldThisFileInfo = $getID3->analyze($this->filename);
|
||||||
return false;
|
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
||||||
}
|
$this->errors[] = 'Cannot write Real tags on old-style file format';
|
||||||
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
fclose($fp_source);
|
||||||
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
return false;
|
||||||
}
|
}
|
||||||
if (!empty($oldChunkInfo['CONT']['length'])) {
|
|
||||||
$this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
|
|
||||||
}
|
|
||||||
|
|
||||||
$new_CONT_tag_data = $this->GenerateCONTchunk();
|
if (empty($OldThisFileInfo['real']['chunks'])) {
|
||||||
$new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
|
$this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
|
||||||
$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
|
fclose($fp_source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
||||||
|
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
||||||
|
}
|
||||||
|
if (!empty($oldChunkInfo['CONT']['length'])) {
|
||||||
|
$this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
|
$new_CONT_tag_data = $this->GenerateCONTchunk();
|
||||||
fseek($fp_source, $oldChunkInfo['.RMF']['offset']);
|
$new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
|
||||||
fwrite($fp_source, $new__RMF_tag_data);
|
$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
|
||||||
} else {
|
|
||||||
$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
|
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
|
if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
|
||||||
fseek($fp_source, $oldChunkInfo['PROP']['offset']);
|
fseek($fp_source, $oldChunkInfo['.RMF']['offset']);
|
||||||
fwrite($fp_source, $new_PROP_tag_data);
|
fwrite($fp_source, $new__RMF_tag_data);
|
||||||
} else {
|
} else {
|
||||||
$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
|
$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
|
||||||
fclose($fp_source);
|
fclose($fp_source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
|
if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
|
||||||
// new data length is same as old data length - just overwrite
|
fseek($fp_source, $oldChunkInfo['PROP']['offset']);
|
||||||
fseek($fp_source, $oldChunkInfo['CONT']['offset']);
|
fwrite($fp_source, $new_PROP_tag_data);
|
||||||
fwrite($fp_source, $new_CONT_tag_data);
|
} else {
|
||||||
fclose($fp_source);
|
$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
|
||||||
return true;
|
fclose($fp_source);
|
||||||
} else {
|
return false;
|
||||||
if (empty($oldChunkInfo['CONT'])) {
|
}
|
||||||
// no existing CONT chunk
|
|
||||||
$BeforeOffset = $oldChunkInfo['DATA']['offset'];
|
|
||||||
$AfterOffset = $oldChunkInfo['DATA']['offset'];
|
|
||||||
} else {
|
|
||||||
// new data is longer than old data
|
|
||||||
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
|
||||||
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
|
||||||
}
|
|
||||||
if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
|
|
||||||
if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
|
|
||||||
rewind($fp_source);
|
|
||||||
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
|
||||||
fwrite($fp_temp, $new_CONT_tag_data);
|
|
||||||
fseek($fp_source, $AfterOffset);
|
|
||||||
while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
|
|
||||||
fwrite($fp_temp, $buffer, strlen($buffer));
|
|
||||||
}
|
|
||||||
fclose($fp_temp);
|
|
||||||
|
|
||||||
if (copy($tempfilename, $this->filename)) {
|
if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
|
||||||
unlink($tempfilename);
|
|
||||||
fclose($fp_source);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
unlink($tempfilename);
|
|
||||||
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
|
|
||||||
} else {
|
|
||||||
$this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GenerateRMFchunk(&$chunks)
|
// new data length is same as old data length - just overwrite
|
||||||
{
|
fseek($fp_source, $oldChunkInfo['CONT']['offset']);
|
||||||
$oldCONTexists = false;
|
fwrite($fp_source, $new_CONT_tag_data);
|
||||||
foreach ($chunks as $key => $chunk) {
|
fclose($fp_source);
|
||||||
$chunkNameKeys[$chunk['name']] = $key;
|
return true;
|
||||||
if ($chunk['name'] == 'CONT') {
|
|
||||||
$oldCONTexists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
|
|
||||||
|
|
||||||
$RMFchunk = "\x00\x00"; // object version
|
} else {
|
||||||
$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
|
if (empty($oldChunkInfo['CONT'])) {
|
||||||
return $RMFchunk;
|
// no existing CONT chunk
|
||||||
}
|
$BeforeOffset = $oldChunkInfo['DATA']['offset'];
|
||||||
|
$AfterOffset = $oldChunkInfo['DATA']['offset'];
|
||||||
|
} else {
|
||||||
|
// new data is longer than old data
|
||||||
|
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
||||||
|
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
||||||
|
}
|
||||||
|
if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
|
||||||
|
if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
|
||||||
|
|
||||||
public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data)
|
rewind($fp_source);
|
||||||
{
|
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
||||||
$old_CONT_length = 0;
|
fwrite($fp_temp, $new_CONT_tag_data);
|
||||||
$old_DATA_offset = 0;
|
fseek($fp_source, $AfterOffset);
|
||||||
$old_INDX_offset = 0;
|
while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
|
||||||
foreach ($chunks as $key => $chunk) {
|
fwrite($fp_temp, $buffer, strlen($buffer));
|
||||||
$chunkNameKeys[$chunk['name']] = $key;
|
}
|
||||||
if ($chunk['name'] == 'CONT') {
|
fclose($fp_temp);
|
||||||
$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
|
if (copy($tempfilename, $this->filename)) {
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
|
unlink($tempfilename);
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
|
fclose($fp_source);
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
|
return true;
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
|
}
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
|
unlink($tempfilename);
|
||||||
$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
|
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
|
||||||
$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
|
} else {
|
||||||
return $PROPchunk;
|
$this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fclose($fp_source);
|
||||||
|
return false;
|
||||||
|
|
||||||
public 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
|
}
|
||||||
|
$this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0), 2);
|
public function GenerateRMFchunk(&$chunks) {
|
||||||
$CONTchunk .= (!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '');
|
$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);
|
||||||
|
|
||||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0), 2);
|
$RMFchunk = "\x00\x00"; // object version
|
||||||
$CONTchunk .= (!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '');
|
$RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
|
||||||
|
$RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4);
|
||||||
|
|
||||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
|
$RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
|
||||||
$CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
|
return $RMFchunk;
|
||||||
|
}
|
||||||
|
|
||||||
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0), 2);
|
public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
|
||||||
$CONTchunk .= (!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '');
|
$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;
|
||||||
|
|
||||||
if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
|
$PROPchunk = "\x00\x00"; // object version
|
||||||
$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
|
$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);
|
||||||
|
|
||||||
$CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
|
$PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
|
||||||
|
return $PROPchunk;
|
||||||
|
}
|
||||||
|
|
||||||
return $CONTchunk;
|
public function GenerateCONTchunk() {
|
||||||
}
|
foreach ($this->tag_data as $key => $value) {
|
||||||
|
// limit each value to 0xFFFF bytes
|
||||||
|
$this->tag_data[$key] = substr($value, 0, 65535);
|
||||||
|
}
|
||||||
|
|
||||||
public function RemoveReal()
|
$CONTchunk = "\x00\x00"; // object version
|
||||||
{
|
|
||||||
// File MUST be writeable - CHMOD(646) at least
|
|
||||||
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
|
||||||
// Initialize getID3 engine
|
|
||||||
$getID3 = new getID3;
|
|
||||||
$OldThisFileInfo = $getID3->analyze($this->filename);
|
|
||||||
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
|
||||||
$this->errors[] = 'Cannot remove Real tags from old-style file format';
|
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($OldThisFileInfo['real']['chunks'])) {
|
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0), 2);
|
||||||
$this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
|
$CONTchunk .= (!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '');
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
|
||||||
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($oldChunkInfo['CONT'])) {
|
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0), 2);
|
||||||
// no existing CONT chunk
|
$CONTchunk .= (!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '');
|
||||||
fclose($fp_source);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
|
||||||
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
$CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
|
||||||
if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
|
|
||||||
if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
|
$CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0), 2);
|
||||||
rewind($fp_source);
|
$CONTchunk .= (!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '');
|
||||||
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
|
||||||
fseek($fp_source, $AfterOffset);
|
if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
|
||||||
while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
|
$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
|
||||||
fwrite($fp_temp, $buffer, strlen($buffer));
|
}
|
||||||
}
|
|
||||||
fclose($fp_temp);
|
$CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
|
||||||
|
|
||||||
|
return $CONTchunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RemoveReal() {
|
||||||
|
// File MUST be writeable - CHMOD(646) at least
|
||||||
|
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
|
||||||
|
|
||||||
|
// Initialize getID3 engine
|
||||||
|
$getID3 = new getID3;
|
||||||
|
$OldThisFileInfo = $getID3->analyze($this->filename);
|
||||||
|
if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
|
||||||
|
$this->errors[] = 'Cannot remove Real tags from old-style file format';
|
||||||
|
fclose($fp_source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($OldThisFileInfo['real']['chunks'])) {
|
||||||
|
$this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
|
||||||
|
fclose($fp_source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
|
||||||
|
$oldChunkInfo[$chunkarray['name']] = $chunkarray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($oldChunkInfo['CONT'])) {
|
||||||
|
// no existing CONT chunk
|
||||||
|
fclose($fp_source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$BeforeOffset = $oldChunkInfo['CONT']['offset'];
|
||||||
|
$AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
|
||||||
|
if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
|
||||||
|
if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
|
||||||
|
|
||||||
|
rewind($fp_source);
|
||||||
|
fwrite($fp_temp, fread($fp_source, $BeforeOffset));
|
||||||
|
fseek($fp_source, $AfterOffset);
|
||||||
|
while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
|
||||||
|
fwrite($fp_temp, $buffer, strlen($buffer));
|
||||||
|
}
|
||||||
|
fclose($fp_temp);
|
||||||
|
|
||||||
|
if (copy($tempfilename, $this->filename)) {
|
||||||
|
unlink($tempfilename);
|
||||||
|
fclose($fp_source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
unlink($tempfilename);
|
||||||
|
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($fp_source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (copy($tempfilename, $this->filename)) {
|
|
||||||
unlink($tempfilename);
|
|
||||||
fclose($fp_source);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
unlink($tempfilename);
|
|
||||||
$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
|
|
||||||
} else {
|
|
||||||
$this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose($fp_source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,97 +18,103 @@
|
||||||
class getid3_write_vorbiscomment
|
class getid3_write_vorbiscomment
|
||||||
{
|
{
|
||||||
|
|
||||||
public $filename;
|
public $filename;
|
||||||
public $tag_data;
|
public $tag_data;
|
||||||
public $warnings = []; // any non-critical errors will be stored here
|
public $warnings = array(); // any non-critical errors will be stored here
|
||||||
public $errors = []; // any critical errors will be stored here
|
public $errors = array(); // any critical errors will be stored here
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function WriteVorbisComment()
|
public function WriteVorbisComment() {
|
||||||
{
|
|
||||||
|
|
||||||
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
|
||||||
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
|
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create file with new comments
|
// Create file with new comments
|
||||||
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
$tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3');
|
||||||
if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) {
|
if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) {
|
||||||
foreach ($this->tag_data as $key => $value) {
|
|
||||||
foreach ($value as $commentdata) {
|
|
||||||
fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose($fpcomments);
|
|
||||||
} else {
|
|
||||||
$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldignoreuserabort = ignore_user_abort(true);
|
foreach ($this->tag_data as $key => $value) {
|
||||||
if (GETID3_OS_ISWINDOWS) {
|
foreach ($value as $commentdata) {
|
||||||
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
|
fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n");
|
||||||
//$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
|
fclose($fpcomments);
|
||||||
// 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
|
} else {
|
||||||
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
|
||||||
clearstatcache();
|
return false;
|
||||||
$timestampbeforewriting = filemtime($this->filename);
|
}
|
||||||
|
|
||||||
$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
$oldignoreuserabort = ignore_user_abort(true);
|
||||||
$VorbiscommentError = `$commandline`;
|
if (GETID3_OS_ISWINDOWS) {
|
||||||
|
|
||||||
if (empty($VorbiscommentError)) {
|
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
|
||||||
clearstatcache();
|
//$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
|
||||||
if ($timestampbeforewriting == filemtime($this->filename)) {
|
// vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
|
||||||
$VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
|
// 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,
|
||||||
} else {
|
// and therefore does not need to be quoted
|
||||||
$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
|
// On top of that, if error messages are not always captured properly under Windows
|
||||||
unlink($tempcommentsfilename);
|
// To at least see if there was a problem, compare file modification timestamps before and after writing
|
||||||
ignore_user_abort($oldignoreuserabort);
|
clearstatcache();
|
||||||
|
$timestampbeforewriting = filemtime($this->filename);
|
||||||
|
|
||||||
if (!empty($VorbiscommentError)) {
|
$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||||
$this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError;
|
$VorbiscommentError = `$commandline`;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
public function DeleteVorbisComment()
|
} else {
|
||||||
{
|
|
||||||
$this->tag_data = [[]];
|
|
||||||
return $this->WriteVorbisComment();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function CleanVorbisCommentName($originalcommentname)
|
$commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
|
||||||
{
|
$VorbiscommentError = `$commandline`;
|
||||||
// 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).
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function DeleteVorbisComment() {
|
||||||
|
$this->tag_data = array(array());
|
||||||
|
return $this->WriteVorbisComment();
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,6 +463,11 @@ http://www.getid3.org/phpBB3/viewtopic.php?t=25
|
||||||
written just "value" (detected by getID3())
|
written just "value" (detected by getID3())
|
||||||
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
|
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
|
||||||
actually ABR or VBR.
|
actually ABR or VBR.
|
||||||
|
* iTunes (versions "v7.0.0.70" is known-guilty, probably
|
||||||
|
other versions are too) writes ID3v2.3 comment tags using an
|
||||||
|
ID3v2.2 frame name (3-bytes) null-padded to 4 bytes which is
|
||||||
|
not valid for ID3v2.3+
|
||||||
|
(detected by getID3() since 1.9.12-201603221746)
|
||||||
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
|
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
|
||||||
other versions are too) writes ID3v2.3 comment tags using a
|
other versions are too) writes ID3v2.3 comment tags using a
|
||||||
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
|
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
|
||||||
|
@ -615,3 +620,4 @@ Reference material:
|
||||||
* http://wyday.com/cuesharp/specification.php
|
* http://wyday.com/cuesharp/specification.php
|
||||||
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
|
||||||
* http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
|
* http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
|
||||||
|
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||||
|
|
Loading…
Reference in a new issue