Update getID3 library to 1.9.8.

Changes:
https://github.com/JamesHeinrich/getID3/compare/1.9.7...v1.9.8

Fixes #29627.

Built from https://develop.svn.wordpress.org/trunk@29734


git-svn-id: http://core.svn.wordpress.org/trunk@29508 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Scott Taylor 2014-09-11 19:07:17 +00:00
parent 293e344490
commit ac02e9951e
19 changed files with 918 additions and 587 deletions

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// getid3.lib.php - part of getID3() // // getid3.lib.php - part of getID3() //
@ -282,7 +283,6 @@ class getid3_lib
} }
} else { } else {
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()'); throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
break;
} }
} }
return self::CastAsInt($intvalue); return self::CastAsInt($intvalue);
@ -635,7 +635,7 @@ class getid3_lib
} }
if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) { if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
if (($fp_dest = fopen($filename_dest, 'wb'))) { if (($fp_dest = fopen($filename_dest, 'wb'))) {
if (fseek($fp_src, $offset, SEEK_SET) == 0) { if (fseek($fp_src, $offset) == 0) {
$byteslefttowrite = $length; $byteslefttowrite = $length;
while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) { while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
$byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
@ -986,6 +986,19 @@ class getid3_lib
throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
} }
public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
if (is_string($data)) {
return self::MultiByteCharString2HTML($data, $charset);
} elseif (is_array($data)) {
$return_data = array();
foreach ($data as $key => $value) {
$return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset);
}
return $return_data;
}
// integer, float, objects, resources, etc
return $data;
}
public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
$string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
@ -1210,16 +1223,21 @@ class getid3_lib
$newvaluelength = strlen(trim($value)); $newvaluelength = strlen(trim($value));
foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
$oldvaluelength = strlen(trim($existingvalue)); $oldvaluelength = strlen(trim($existingvalue));
if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
break 2; //break 2;
break;
} }
} }
} }
if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
$value = (is_string($value) ? trim($value) : $value); $value = (is_string($value) ? trim($value) : $value);
$ThisFileInfo['comments'][$tagname][] = $value; if (!is_numeric($key)) {
$ThisFileInfo['comments'][$tagname][$key] = $value;
} else {
$ThisFileInfo['comments'][$tagname][] = $value;
}
} }
} }
} }
@ -1227,20 +1245,23 @@ class getid3_lib
} }
// Copy to ['comments_html'] // Copy to ['comments_html']
foreach ($ThisFileInfo['comments'] as $field => $values) { if (!empty($ThisFileInfo['comments'])) {
if ($field == 'picture') { foreach ($ThisFileInfo['comments'] as $field => $values) {
// pictures can take up a lot of space, and we don't need multiple copies of them if ($field == 'picture') {
// let there be a single copy in [comments][picture], and not elsewhere // pictures can take up a lot of space, and we don't need multiple copies of them
continue; // let there be a single copy in [comments][picture], and not elsewhere
} continue;
foreach ($values as $index => $value) { }
if (is_array($value)) { foreach ($values as $index => $value) {
$ThisFileInfo['comments_html'][$field][$index] = $value; if (is_array($value)) {
} else { $ThisFileInfo['comments_html'][$field][$index] = $value;
$ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); } else {
$ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
}
} }
} }
} }
} }
return true; return true;
} }
@ -1339,4 +1360,17 @@ class getid3_lib
} }
return $filesize; return $filesize;
} }
/**
* Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
* @param string $path A path.
* @param string $suffix If the name component ends in suffix this will also be cut off.
* @return string
*/
public static function mb_basename($path, $suffix = null) {
$splited = preg_split('#/#', rtrim($path, '/ '));
return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
}
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Please see readme.txt for more information // // Please see readme.txt for more information //
@ -17,18 +18,21 @@ if (!defined('GETID3_OS_ISWINDOWS')) {
if (!defined('GETID3_INCLUDEPATH')) { if (!defined('GETID3_INCLUDEPATH')) {
define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR); define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
} }
// Workaround Bug #39923 (https://bugs.php.net/bug.php?id=39923)
if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
define('IMG_JPG', IMAGETYPE_JPEG);
}
// attempt to define temp dir as something flexible but reliable // attempt to define temp dir as something flexible but reliable
$temp_dir = ini_get('upload_tmp_dir'); $temp_dir = ini_get('upload_tmp_dir');
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) { if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
$temp_dir = ''; $temp_dir = '';
} }
if (!$temp_dir && function_exists('sys_get_temp_dir')) { if (!$temp_dir) {
// PHP v5.2.1+
// sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
$temp_dir = sys_get_temp_dir(); $temp_dir = sys_get_temp_dir();
} }
$temp_dir = realpath($temp_dir); $temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10
$open_basedir = ini_get('open_basedir'); $open_basedir = ini_get('open_basedir');
if ($open_basedir) { if ($open_basedir) {
// e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/" // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
@ -57,7 +61,9 @@ if (!$temp_dir) {
$temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
} }
// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system // $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
define('GETID3_TEMP_DIR', $temp_dir); if (!defined('GETID3_TEMP_DIR')) {
define('GETID3_TEMP_DIR', $temp_dir);
}
unset($open_basedir, $temp_dir); unset($open_basedir, $temp_dir);
// End: Defines // End: Defines
@ -97,13 +103,13 @@ class getID3
public $fp; // Filepointer to file being analysed. public $fp; // Filepointer to file being analysed.
public $info; // Result array. public $info; // Result array.
public $tempdir = GETID3_TEMP_DIR; public $tempdir = GETID3_TEMP_DIR;
public $memory_limit = 0;
// Protected variables // Protected variables
protected $startup_error = ''; protected $startup_error = '';
protected $startup_warning = ''; protected $startup_warning = '';
protected $memory_limit = 0;
const VERSION = '1.9.7-20130705'; const VERSION = '1.9.8-20140511';
const FREAD_BUFFER_SIZE = 32768; const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false; const ATTACHMENTS_NONE = false;
@ -112,13 +118,6 @@ class getID3
// public: constructor // public: constructor
public function __construct() { public function __construct() {
// Check for PHP version
$required_php_version = '5.0.5';
if (version_compare(PHP_VERSION, $required_php_version, '<')) {
$this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
return false;
}
// Check memory // Check memory
$this->memory_limit = ini_get('memory_limit'); $this->memory_limit = ini_get('memory_limit');
if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) { if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
@ -261,16 +260,32 @@ class getID3
$filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename); $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
// open local file // open local file
if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720
if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
// great // great
} else { } else {
throw new getid3_exception('Could not open "'.$filename.'" (does not exist, or is not a file)'); $errormessagelist = array();
if (!is_readable($filename)) {
$errormessagelist[] = '!is_readable';
}
if (!is_file($filename)) {
$errormessagelist[] = '!is_file';
}
if (!file_exists($filename)) {
$errormessagelist[] = '!file_exists';
}
if (empty($errormessagelist)) {
$errormessagelist[] = 'fopen failed';
}
throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
} }
$this->info['filesize'] = filesize($filename); $this->info['filesize'] = filesize($filename);
// set redundant parameters - might be needed in some include file // set redundant parameters - might be needed in some include file
$this->info['filename'] = basename($filename); // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
$filename = str_replace('\\', '/', $filename);
$this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
$this->info['filename'] = getid3_lib::mb_basename($filename);
$this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
@ -352,7 +367,7 @@ class getID3
// ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
if (!$this->option_tag_id3v2) { if (!$this->option_tag_id3v2) {
fseek($this->fp, 0, SEEK_SET); fseek($this->fp, 0);
$header = fread($this->fp, 10); $header = fread($this->fp, 10);
if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) { if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
$this->info['id3v2']['header'] = true; $this->info['id3v2']['header'] = true;
@ -363,7 +378,7 @@ class getID3
} }
// read 32 kb file data // read 32 kb file data
fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); fseek($this->fp, $this->info['avdataoffset']);
$formattest = fread($this->fp, 32774); $formattest = fread($this->fp, 32774);
// determine format // determine format
@ -588,6 +603,14 @@ class getID3
'mime_type' => 'audio/basic', 'mime_type' => 'audio/basic',
), ),
// AMR - audio - Adaptive Multi Rate
'amr' => array(
'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A]
'group' => 'audio',
'module' => 'amr',
'mime_type' => 'audio/amr',
),
// AVR - audio - Audio Visual Research // AVR - audio - Audio Visual Research
'avr' => array( 'avr' => array(
'pattern' => '^2BIT', 'pattern' => '^2BIT',
@ -1161,6 +1184,7 @@ class getID3
'matroska' => array('matroska' , 'UTF-8'), 'matroska' => array('matroska' , 'UTF-8'),
'flac' => array('vorbiscomment' , 'UTF-8'), 'flac' => array('vorbiscomment' , 'UTF-8'),
'divxtag' => array('divx' , 'ISO-8859-1'), 'divxtag' => array('divx' , 'ISO-8859-1'),
'iptc' => array('iptc' , 'ISO-8859-1'),
); );
} }
@ -1181,7 +1205,11 @@ class getID3
$value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed! $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
} }
if ($value) { if ($value) {
$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; if (!is_numeric($key)) {
$this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
} else {
$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
}
} }
} }
if ($tag_key == 'picture') { if ($tag_key == 'picture') {
@ -1196,14 +1224,7 @@ class getID3
if ($this->option_tags_html) { if ($this->option_tags_html) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) { $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
if (is_string($value)) {
//$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding)));
} else {
$this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
}
}
} }
} }
@ -1259,7 +1280,6 @@ class getID3
return true; return true;
} }
public function getHashdata($algorithm) { public function getHashdata($algorithm) {
switch ($algorithm) { switch ($algorithm) {
case 'md5': case 'md5':
@ -1565,8 +1585,11 @@ class getID3
} }
abstract class getid3_handler abstract class getid3_handler {
{
/**
* @var getID3
*/
protected $getid3; // pointer protected $getid3; // pointer
protected $data_string_flag = false; // analyzing filepointer or string protected $data_string_flag = false; // analyzing filepointer or string
@ -1593,7 +1616,7 @@ abstract class getid3_handler
// Analyze from string instead // Analyze from string instead
public function AnalyzeString($string) { public function AnalyzeString($string) {
// Enter string mode // Enter string mode
$this->setStringMode($string); $this->setStringMode($string);
// Save info // Save info
$saved_avdataoffset = $this->getid3->info['avdataoffset']; $saved_avdataoffset = $this->getid3->info['avdataoffset'];
@ -1634,10 +1657,10 @@ abstract class getid3_handler
$this->data_string_position += $bytes; $this->data_string_position += $bytes;
return substr($this->data_string, $this->data_string_position - $bytes, $bytes); return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
} }
$pos = $this->ftell() + $bytes; $pos = $this->ftell() + $bytes;
if (!getid3_lib::intValueSupported($pos)) { if (!getid3_lib::intValueSupported($pos)) {
throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10); throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
} }
return fread($this->getid3->fp, $bytes); return fread($this->getid3->fp, $bytes);
} }
@ -1657,14 +1680,14 @@ abstract class getid3_handler
break; break;
} }
return 0; return 0;
} else { } else {
$pos = $bytes; $pos = $bytes;
if ($whence == SEEK_CUR) { if ($whence == SEEK_CUR) {
$pos = $this->ftell() + $bytes; $pos = $this->ftell() + $bytes;
} elseif ($whence == SEEK_END) { } elseif ($whence == SEEK_END) {
$pos = $this->info['filesize'] + $bytes; $pos = $this->getid3->info['filesize'] + $bytes;
} }
if (!getid3_lib::intValueSupported($pos)) { if (!getid3_lib::intValueSupported($pos)) {
throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10); throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
} }
} }
@ -1682,20 +1705,17 @@ abstract class getid3_handler
return $this->dependency_to == $module; return $this->dependency_to == $module;
} }
protected function error($text) protected function error($text) {
{
$this->getid3->info['error'][] = $text; $this->getid3->info['error'][] = $text;
return false; return false;
} }
protected function warning($text) protected function warning($text) {
{
return $this->getid3->warning($text); return $this->getid3->warning($text);
} }
protected function notice($text) protected function notice($text) {
{
// does nothing for now // does nothing for now
} }

View File

@ -24,4 +24,4 @@ made publicly available to all getID3() users.
The licensee may not sub-license getID3() itself, meaning that any The licensee may not sub-license getID3() itself, meaning that any
commercially released product containing all or parts of getID3() must commercially released product containing all or parts of getID3() must
have added functionality beyond what is available in getID3(); have added functionality beyond what is available in getID3();
getID3() itself may not be re-licensed by the licensee. getID3() itself may not be re-licensed by the licensee.

View File

@ -2,6 +2,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
***************************************************************** *****************************************************************
@ -25,4 +26,4 @@ getID3 Commercial License: http://getid3.org/#gCL (payment required)
***************************************************************** *****************************************************************
Copies of each of the above licenses are included in the 'licenses' Copies of each of the above licenses are included in the 'licenses'
directory of the getID3 distribution. directory of the getID3 distribution.

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -15,8 +16,7 @@
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_asf extends getid3_handler class getid3_asf extends getid3_handler {
{
public function __construct(getID3 $getid3) { public function __construct(getID3 $getid3) {
parent::__construct($getid3); // extends getid3_handler::__construct() parent::__construct($getid3); // extends getid3_handler::__construct()
@ -66,25 +66,22 @@ class getid3_asf extends getid3_handler
$info['fileformat'] = 'asf'; $info['fileformat'] = 'asf';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$HeaderObjectData = fread($this->getid3->fp, 30); $HeaderObjectData = $this->fread(30);
$thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16);
$thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']);
if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) {
$info['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; unset($info['fileformat'], $info['asf']);
unset($info['fileformat']); return $this->error('ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}');
unset($info['asf']);
return false;
break;
} }
$thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8)); $thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8));
$thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4)); $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4));
$thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1));
$thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1));
$NextObjectOffset = ftell($this->getid3->fp); $NextObjectOffset = $this->ftell();
$ASFHeaderData = fread($this->getid3->fp, $thisfile_asf_headerobject['objectsize'] - 30); $ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30);
$offset = 0; $offset = 0;
for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
@ -284,7 +281,7 @@ class getid3_asf extends getid3_handler
$offset += 4; $offset += 4;
$thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']);
$unhandled_sections = 0; $unhandled_sections = 0;
$thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->ASF_HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections); $thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections);
if ($unhandled_sections === 0) { if ($unhandled_sections === 0) {
unset($thisfile_asf_headerextensionobject['extension_data']); unset($thisfile_asf_headerextensionobject['extension_data']);
} }
@ -332,7 +329,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
$offset += 2; $offset += 2;
$thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']); $thisfile_asf_codeclistobject_codecentries_current['type'] = self::codecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']);
$CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
$offset += 2; $offset += 2;
@ -826,22 +823,14 @@ class getid3_asf extends getid3_handler
break; break;
case 'id3': case 'id3':
// id3v2 module might not be loaded $this->getid3->include_module('tag.id3v2');
if (class_exists('getid3_id3v2')) {
$tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
$tempfilehandle = fopen($tempfile, 'wb');
$tempThisfileInfo = array('encoding'=>$info['encoding']);
fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
fclose($tempfilehandle);
$getid3_temp = new getID3(); $getid3_id3v2 = new getid3_id3v2($this->getid3);
$getid3_temp->openfile($tempfile); $getid3_id3v2->AnalyzeString($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
$getid3_id3v2 = new getid3_id3v2($getid3_temp); unset($getid3_id3v2);
$getid3_id3v2->Analyze();
$info['id3v2'] = $getid3_temp->info['id3v2'];
unset($getid3_temp, $getid3_id3v2);
unlink($tempfile); if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] > 1024) {
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = '<value too large to display>';
} }
break; break;
@ -860,7 +849,7 @@ class getid3_asf extends getid3_handler
$wm_picture_offset = 0; $wm_picture_offset = 0;
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1));
$wm_picture_offset += 1; $wm_picture_offset += 1;
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = $this->WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = self::WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']);
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4)); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4));
$wm_picture_offset += 4; $wm_picture_offset += 4;
@ -1156,8 +1145,8 @@ class getid3_asf extends getid3_handler
} }
} }
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
$NextObjectDataHeader = fread($this->getid3->fp, 24); $NextObjectDataHeader = $this->fread(24);
$offset = 0; $offset = 0;
$NextObjectGUID = substr($NextObjectDataHeader, 0, 16); $NextObjectGUID = substr($NextObjectDataHeader, 0, 16);
$offset += 16; $offset += 16;
@ -1179,7 +1168,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['data_object'] = array(); $thisfile_asf['data_object'] = array();
$thisfile_asf_dataobject = &$thisfile_asf['data_object']; $thisfile_asf_dataobject = &$thisfile_asf['data_object'];
$DataObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 50 - 24); $DataObjectData = $NextObjectDataHeader.$this->fread(50 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_dataobject['objectid'] = $NextObjectGUID; $thisfile_asf_dataobject['objectid'] = $NextObjectGUID;
@ -1207,9 +1196,9 @@ class getid3_asf extends getid3_handler
// * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure
// * Error Correction Data // * Error Correction Data
$info['avdataoffset'] = ftell($this->getid3->fp); $info['avdataoffset'] = $this->ftell();
fseek($this->getid3->fp, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data $this->fseek(($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data
$info['avdataend'] = ftell($this->getid3->fp); $info['avdataend'] = $this->ftell();
break; break;
case GETID3_ASF_Simple_Index_Object: case GETID3_ASF_Simple_Index_Object:
@ -1229,7 +1218,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['simple_index_object'] = array(); $thisfile_asf['simple_index_object'] = array();
$thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object'];
$SimpleIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 56 - 24); $SimpleIndexObjectData = $NextObjectDataHeader.$this->fread(56 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID;
@ -1246,7 +1235,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$IndexEntriesData = $SimpleIndexObjectData.fread($this->getid3->fp, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); $IndexEntriesData = $SimpleIndexObjectData.$this->fread(6 * $thisfile_asf_simpleindexobject['index_entries_count']);
for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) {
$thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4));
$offset += 4; $offset += 4;
@ -1283,7 +1272,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['asf_index_object'] = array(); $thisfile_asf['asf_index_object'] = array();
$thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object'];
$ASFIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 34 - 24); $ASFIndexObjectData = $NextObjectDataHeader.$this->fread(34 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID;
@ -1297,7 +1286,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count']);
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2));
$offset += 2; $offset += 2;
@ -1307,17 +1296,17 @@ class getid3_asf extends getid3_handler
$thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']);
} }
$ASFIndexObjectData .= fread($this->getid3->fp, 4); $ASFIndexObjectData .= $this->fread(4);
$thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$ASFIndexObjectData .= fread($this->getid3->fp, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); $ASFIndexObjectData .= $this->fread(8 * $thisfile_asf_asfindexobject['index_specifiers_count']);
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8));
$offset += 8; $offset += 8;
} }
$ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']);
for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) {
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
@ -1332,9 +1321,9 @@ class getid3_asf extends getid3_handler
if ($this->GUIDname($NextObjectGUIDtext)) { if ($this->GUIDname($NextObjectGUIDtext)) {
$info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8);
} else { } else {
$info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($this->getid3->fp) - 16 - 8); $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.($this->ftell() - 16 - 8);
} }
fseek($this->getid3->fp, ($NextObjectSize - 16 - 8), SEEK_CUR); $this->fseek(($NextObjectSize - 16 - 8), SEEK_CUR);
break; break;
} }
} }
@ -1433,10 +1422,10 @@ class getid3_asf extends getid3_handler
$thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf');
} }
if (!empty($thisfile_video['streams'])) { if (!empty($thisfile_video['streams'])) {
$thisfile_video['streams']['resolution_x'] = 0; $thisfile_video['resolution_x'] = 0;
$thisfile_video['streams']['resolution_y'] = 0; $thisfile_video['resolution_y'] = 0;
foreach ($thisfile_video['streams'] as $key => $valuearray) { foreach ($thisfile_video['streams'] as $key => $valuearray) {
if (($valuearray['resolution_x'] > $thisfile_video['streams']['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['streams']['resolution_y'])) { if (($valuearray['resolution_x'] > $thisfile_video['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['resolution_y'])) {
$thisfile_video['resolution_x'] = $valuearray['resolution_x']; $thisfile_video['resolution_x'] = $valuearray['resolution_x'];
$thisfile_video['resolution_y'] = $valuearray['resolution_y']; $thisfile_video['resolution_y'] = $valuearray['resolution_y'];
} }
@ -1451,15 +1440,14 @@ class getid3_asf extends getid3_handler
return true; return true;
} }
public static function ASFCodecListObjectTypeLookup($CodecListType) { public static function codecListObjectTypeLookup($CodecListType) {
static $ASFCodecListObjectTypeLookup = array(); static $lookup = array(
if (empty($ASFCodecListObjectTypeLookup)) { 0x0001 => 'Video Codec',
$ASFCodecListObjectTypeLookup[0x0001] = 'Video Codec'; 0x0002 => 'Audio Codec',
$ASFCodecListObjectTypeLookup[0x0002] = 'Audio Codec'; 0xFFFF => 'Unknown Codec'
$ASFCodecListObjectTypeLookup[0xFFFF] = 'Unknown Codec'; );
}
return (isset($ASFCodecListObjectTypeLookup[$CodecListType]) ? $ASFCodecListObjectTypeLookup[$CodecListType] : 'Invalid Codec Type'); return (isset($lookup[$CodecListType]) ? $lookup[$CodecListType] : 'Invalid Codec Type');
} }
public static function KnownGUIDs() { public static function KnownGUIDs() {
@ -1666,31 +1654,37 @@ class getid3_asf extends getid3_handler
} }
public static function WMpictureTypeLookup($WMpictureType) { public static function WMpictureTypeLookup($WMpictureType) {
static $WMpictureTypeLookup = array(); static $lookup = null;
if (empty($WMpictureTypeLookup)) { if ($lookup === null) {
$WMpictureTypeLookup[0x03] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Front Cover'); $lookup = array(
$WMpictureTypeLookup[0x04] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Back Cover'); 0x03 => 'Front Cover',
$WMpictureTypeLookup[0x00] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'User Defined'); 0x04 => 'Back Cover',
$WMpictureTypeLookup[0x05] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Leaflet Page'); 0x00 => 'User Defined',
$WMpictureTypeLookup[0x06] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Media Label'); 0x05 => 'Leaflet Page',
$WMpictureTypeLookup[0x07] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lead Artist'); 0x06 => 'Media Label',
$WMpictureTypeLookup[0x08] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Artist'); 0x07 => 'Lead Artist',
$WMpictureTypeLookup[0x09] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Conductor'); 0x08 => 'Artist',
$WMpictureTypeLookup[0x0A] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band'); 0x09 => 'Conductor',
$WMpictureTypeLookup[0x0B] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Composer'); 0x0A => 'Band',
$WMpictureTypeLookup[0x0C] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lyricist'); 0x0B => 'Composer',
$WMpictureTypeLookup[0x0D] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Recording Location'); 0x0C => 'Lyricist',
$WMpictureTypeLookup[0x0E] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Recording'); 0x0D => 'Recording Location',
$WMpictureTypeLookup[0x0F] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Performance'); 0x0E => 'During Recording',
$WMpictureTypeLookup[0x10] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Video Screen Capture'); 0x0F => 'During Performance',
$WMpictureTypeLookup[0x12] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Illustration'); 0x10 => 'Video Screen Capture',
$WMpictureTypeLookup[0x13] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band Logotype'); 0x12 => 'Illustration',
$WMpictureTypeLookup[0x14] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Publisher Logotype'); 0x13 => 'Band Logotype',
0x14 => 'Publisher Logotype'
);
$lookup = array_map(function($str) {
return getid3_lib::iconv_fallback('UTF-8', 'UTF-16LE', $str);
}, $lookup);
} }
return (isset($WMpictureTypeLookup[$WMpictureType]) ? $WMpictureTypeLookup[$WMpictureType] : '');
return (isset($lookup[$WMpictureType]) ? $lookup[$WMpictureType] : '');
} }
public function ASF_HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) {
// http://msdn.microsoft.com/en-us/library/bb643323.aspx // http://msdn.microsoft.com/en-us/library/bb643323.aspx
$offset = 0; $offset = 0;
@ -1825,7 +1819,7 @@ class getid3_asf extends getid3_handler
$descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2));
$offset += 2; $offset += 2;
$descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']);
$descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4));
$offset += 4; $offset += 4;
@ -1897,7 +1891,7 @@ class getid3_asf extends getid3_handler
$descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2));
$offset += 2; $offset += 2;
$descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']);
$descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4));
$offset += 4; $offset += 4;
@ -1937,8 +1931,8 @@ class getid3_asf extends getid3_handler
} }
public static function ASFmetadataLibraryObjectDataTypeLookup($id) { public static function metadataLibraryObjectDataTypeLookup($id) {
static $ASFmetadataLibraryObjectDataTypeLookup = array( static $lookup = array(
0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters 0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters
0x0001 => 'BYTE array', // The type of the data is implementation-specific 0x0001 => 'BYTE array', // The type of the data is implementation-specific
0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values 0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values
@ -1947,7 +1941,7 @@ class getid3_asf extends getid3_handler
0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer 0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer
0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID 0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID
); );
return (isset($ASFmetadataLibraryObjectDataTypeLookup[$id]) ? $ASFmetadataLibraryObjectDataTypeLookup[$id] : 'invalid'); return (isset($lookup[$id]) ? $lookup[$id] : 'invalid');
} }
public function ASF_WMpicture(&$data) { public function ASF_WMpicture(&$data) {
@ -1964,7 +1958,7 @@ class getid3_asf extends getid3_handler
$offset = 0; $offset = 0;
$WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1)); $WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1));
$offset += 1; $offset += 1;
$WMpicture['image_type'] = $this->WMpictureTypeLookup($WMpicture['image_type_id']); $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']);
$WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4)); $WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4));
$offset += 4; $offset += 4;
@ -2016,4 +2010,4 @@ class getid3_asf extends getid3_handler
return $string; return $string;
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
// // // //
// FLV module by Seth Kaufman <sethØwhirl-i-gig*com> // // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> //
// // // //
@ -38,6 +39,11 @@
// * version 0.6.1 (30 May 2011) // // * version 0.6.1 (30 May 2011) //
// prevent infinite loops in expGolombUe() // // prevent infinite loops in expGolombUe() //
// // // //
// * version 0.7.0 (16 Jul 2013) //
// handle GETID3_FLV_VIDEO_VP6FLV_ALPHA //
// improved AVCSequenceParameterSetReader::readData() //
// by Xander Schouwerwou <schouwerwouØgmail*com> //
// //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// module.audio-video.flv.php // // module.audio-video.flv.php //
@ -67,38 +73,38 @@ define('H264_PROFILE_HIGH422', 122);
define('H264_PROFILE_HIGH444', 144); define('H264_PROFILE_HIGH444', 144);
define('H264_PROFILE_HIGH444_PREDICTIVE', 244); define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
class getid3_flv extends getid3_handler class getid3_flv extends getid3_handler {
{
const magic = 'FLV';
public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
public function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$FLVdataLength = $info['avdataend'] - $info['avdataoffset']; $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
$FLVheader = fread($this->getid3->fp, 5); $FLVheader = $this->fread(5);
$info['fileformat'] = 'flv'; $info['fileformat'] = 'flv';
$info['flv']['header']['signature'] = substr($FLVheader, 0, 3); $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
$info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
$TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
$magic = 'FLV'; if ($info['flv']['header']['signature'] != self::magic) {
if ($info['flv']['header']['signature'] != $magic) { $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; unset($info['flv'], $info['fileformat']);
unset($info['flv']);
unset($info['fileformat']);
return false; return false;
} }
$info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
$info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4)); $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4));
$FLVheaderFrameLength = 9; $FLVheaderFrameLength = 9;
if ($FrameSizeDataLength > $FLVheaderFrameLength) { if ($FrameSizeDataLength > $FLVheaderFrameLength) {
fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
} }
$Duration = 0; $Duration = 0;
$found_video = false; $found_video = false;
@ -108,15 +114,15 @@ class getid3_flv extends getid3_handler
$tagParseCount = 0; $tagParseCount = 0;
$info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
$flv_framecount = &$info['flv']['framecount']; $flv_framecount = &$info['flv']['framecount'];
while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
$ThisTagHeader = fread($this->getid3->fp, 16); $ThisTagHeader = $this->fread(16);
$PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
$TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
$DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
$Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
$LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
$NextOffset = ftell($this->getid3->fp) - 1 + $DataLength; $NextOffset = $this->ftell() - 1 + $DataLength;
if ($Timestamp > $Duration) { if ($Timestamp > $Duration) {
$Duration = $Timestamp; $Duration = $Timestamp;
} }
@ -140,7 +146,7 @@ class getid3_flv extends getid3_handler
$found_video = true; $found_video = true;
$info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
$FLVvideoHeader = fread($this->getid3->fp, 11); $FLVvideoHeader = $this->fread(11);
if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
// this code block contributed by: moysevichØgmail*com // this code block contributed by: moysevichØgmail*com
@ -160,7 +166,7 @@ class getid3_flv extends getid3_handler
//$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
$spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
// read the first SequenceParameterSet // read the first SequenceParameterSet
$sps = fread($this->getid3->fp, $spsSize); $sps = $this->fread($spsSize);
if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
$spsReader = new AVCSequenceParameterSetReader($sps); $spsReader = new AVCSequenceParameterSetReader($sps);
$spsReader->readData(); $spsReader->readData();
@ -185,19 +191,15 @@ class getid3_flv extends getid3_handler
//$PictureSizeEnc <<= 1; //$PictureSizeEnc <<= 1;
//$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7;
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7;
$PictureSizeEnc['x'] >>= 7;
$PictureSizeEnc['y'] >>= 7;
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
break; break;
case 1: case 1:
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)); $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7;
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)); $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7;
$PictureSizeEnc['x'] >>= 7;
$PictureSizeEnc['y'] >>= 7;
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
break; break;
@ -233,8 +235,22 @@ class getid3_flv extends getid3_handler
break; break;
} }
} elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_VP6FLV_ALPHA) {
/* contributed by schouwerwouØgmail*com */
if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2));
$info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3;
$info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3;
}
/* end schouwerwouØgmail*com */
}
if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
$info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
} }
$info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
} }
break; break;
@ -242,8 +258,8 @@ class getid3_flv extends getid3_handler
case GETID3_FLV_TAG_META: case GETID3_FLV_TAG_META:
if (!$found_meta) { if (!$found_meta) {
$found_meta = true; $found_meta = true;
fseek($this->getid3->fp, -1, SEEK_CUR); $this->fseek(-1, SEEK_CUR);
$datachunk = fread($this->getid3->fp, $DataLength); $datachunk = $this->fread($DataLength);
$AMFstream = new AMFStream($datachunk); $AMFstream = new AMFStream($datachunk);
$reader = new AMFReader($AMFstream); $reader = new AMFReader($AMFstream);
$eventName = $reader->readData(); $eventName = $reader->readData();
@ -279,7 +295,7 @@ class getid3_flv extends getid3_handler
// noop // noop
break; break;
} }
fseek($this->getid3->fp, $NextOffset, SEEK_SET); $this->fseek($NextOffset);
} }
$info['playtime_seconds'] = $Duration / 1000; $info['playtime_seconds'] = $Duration / 1000;
@ -288,16 +304,16 @@ class getid3_flv extends getid3_handler
} }
if ($info['flv']['header']['hasAudio']) { if ($info['flv']['header']['hasAudio']) {
$info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']); $info['audio']['codec'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']);
$info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']); $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']);
$info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']); $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']);
$info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
$info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
$info['audio']['dataformat'] = 'flv'; $info['audio']['dataformat'] = 'flv';
} }
if (!empty($info['flv']['header']['hasVideo'])) { if (!empty($info['flv']['header']['hasVideo'])) {
$info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']); $info['video']['codec'] = self::videoCodecLookup($info['flv']['video']['videoCodec']);
$info['video']['dataformat'] = 'flv'; $info['video']['dataformat'] = 'flv';
$info['video']['lossless'] = false; $info['video']['lossless'] = false;
} }
@ -308,17 +324,17 @@ class getid3_flv extends getid3_handler
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
} }
if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
$info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']); $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']);
} }
if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
$info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']); $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']);
} }
return true; return true;
} }
public function FLVaudioFormat($id) { public static function audioFormatLookup($id) {
$FLVaudioFormat = array( static $lookup = array(
0 => 'Linear PCM, platform endian', 0 => 'Linear PCM, platform endian',
1 => 'ADPCM', 1 => 'ADPCM',
2 => 'mp3', 2 => 'mp3',
@ -330,35 +346,35 @@ class getid3_flv extends getid3_handler
8 => 'G.711 mu-law logarithmic PCM', 8 => 'G.711 mu-law logarithmic PCM',
9 => 'reserved', 9 => 'reserved',
10 => 'AAC', 10 => 'AAC',
11 => false, // unknown? 11 => 'Speex',
12 => false, // unknown? 12 => false, // unknown?
13 => false, // unknown? 13 => false, // unknown?
14 => 'mp3 8kHz', 14 => 'mp3 8kHz',
15 => 'Device-specific sound', 15 => 'Device-specific sound',
); );
return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
public function FLVaudioRate($id) { public static function audioRateLookup($id) {
$FLVaudioRate = array( static $lookup = array(
0 => 5500, 0 => 5500,
1 => 11025, 1 => 11025,
2 => 22050, 2 => 22050,
3 => 44100, 3 => 44100,
); );
return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
public function FLVaudioBitDepth($id) { public static function audioBitDepthLookup($id) {
$FLVaudioBitDepth = array( static $lookup = array(
0 => 8, 0 => 8,
1 => 16, 1 => 16,
); );
return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
public function FLVvideoCodec($id) { public static function videoCodecLookup($id) {
$FLVvideoCodec = array( static $lookup = array(
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
GETID3_FLV_VIDEO_SCREEN => 'Screen video', GETID3_FLV_VIDEO_SCREEN => 'Screen video',
GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
@ -366,7 +382,7 @@ class getid3_flv extends getid3_handler
GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
); );
return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
} }
@ -374,7 +390,7 @@ class AMFStream {
public $bytes; public $bytes;
public $pos; public $pos;
public function AMFStream(&$bytes) { public function __construct(&$bytes) {
$this->bytes =& $bytes; $this->bytes =& $bytes;
$this->pos = 0; $this->pos = 0;
} }
@ -457,7 +473,7 @@ class AMFStream {
class AMFReader { class AMFReader {
public $stream; public $stream;
public function AMFReader(&$stream) { public function __construct(&$stream) {
$this->stream =& $stream; $this->stream =& $stream;
} }
@ -619,60 +635,60 @@ class AVCSequenceParameterSetReader {
public $width; public $width;
public $height; public $height;
public function AVCSequenceParameterSetReader($sps) { public function __construct($sps) {
$this->sps = $sps; $this->sps = $sps;
} }
public function readData() { public function readData() {
$this->skipBits(8); $this->skipBits(8);
$this->skipBits(8); $this->skipBits(8);
$profile = $this->getBits(8); // read profile $profile = $this->getBits(8); // read profile
$this->skipBits(16); if ($profile > 0) {
$this->expGolombUe(); // read sps id $this->skipBits(8);
if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) { $level_idc = $this->getBits(8); // level_idc
if ($this->expGolombUe() == 3) { $this->expGolombUe(); // seq_parameter_set_id // sps
$this->skipBits(1); $this->expGolombUe(); // log2_max_frame_num_minus4
} $picOrderType = $this->expGolombUe(); // pic_order_cnt_type
$this->expGolombUe(); if ($picOrderType == 0) {
$this->expGolombUe(); $this->expGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
$this->skipBits(1); } elseif ($picOrderType == 1) {
if ($this->getBit()) { $this->skipBits(1); // delta_pic_order_always_zero_flag
for ($i = 0; $i < 8; $i++) { $this->expGolombSe(); // offset_for_non_ref_pic
if ($this->getBit()) { $this->expGolombSe(); // offset_for_top_to_bottom_field
$size = $i < 6 ? 16 : 64; $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle
$lastScale = 8; for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) {
$nextScale = 8; $this->expGolombSe(); // offset_for_ref_frame[ i ]
for ($j = 0; $j < $size; $j++) {
if ($nextScale != 0) {
$deltaScale = $this->expGolombUe();
$nextScale = ($lastScale + $deltaScale + 256) % 256;
}
if ($nextScale != 0) {
$lastScale = $nextScale;
}
}
}
} }
} }
} $this->expGolombUe(); // num_ref_frames
$this->expGolombUe(); $this->skipBits(1); // gaps_in_frame_num_value_allowed_flag
$pocType = $this->expGolombUe(); $pic_width_in_mbs_minus1 = $this->expGolombUe(); // pic_width_in_mbs_minus1
if ($pocType == 0) { $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1
$this->expGolombUe();
} elseif ($pocType == 1) { $frame_mbs_only_flag = $this->getBits(1); // frame_mbs_only_flag
$this->skipBits(1); if ($frame_mbs_only_flag == 0) {
$this->expGolombSe(); $this->skipBits(1); // mb_adaptive_frame_field_flag
$this->expGolombSe();
$pocCycleLength = $this->expGolombUe();
for ($i = 0; $i < $pocCycleLength; $i++) {
$this->expGolombSe();
} }
$this->skipBits(1); // direct_8x8_inference_flag
$frame_cropping_flag = $this->getBits(1); // frame_cropping_flag
$frame_crop_left_offset = 0;
$frame_crop_right_offset = 0;
$frame_crop_top_offset = 0;
$frame_crop_bottom_offset = 0;
if ($frame_cropping_flag) {
$frame_crop_left_offset = $this->expGolombUe(); // frame_crop_left_offset
$frame_crop_right_offset = $this->expGolombUe(); // frame_crop_right_offset
$frame_crop_top_offset = $this->expGolombUe(); // frame_crop_top_offset
$frame_crop_bottom_offset = $this->expGolombUe(); // frame_crop_bottom_offset
}
$this->skipBits(1); // vui_parameters_present_flag
// etc
$this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2);
$this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2);
} }
$this->expGolombUe();
$this->skipBits(1);
$this->width = ($this->expGolombUe() + 1) * 16;
$heightMap = $this->expGolombUe() + 1;
$this->height = (2 - $this->getBit()) * $heightMap * 16;
} }
public function skipBits($bits) { public function skipBits($bits) {
@ -726,4 +742,4 @@ class AVCSequenceParameterSetReader {
public function getHeight() { public function getHeight() {
return $this->height; return $this->height;
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -281,10 +282,8 @@ class getid3_matroska extends getid3_handler
switch ($trackarray['CodecID']) { switch ($trackarray['CodecID']) {
case 'V_MS/VFW/FOURCC': case 'V_MS/VFW/FOURCC':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$this->warning('Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
break;
}
$parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']); $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
$track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']); $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
@ -335,10 +334,7 @@ class getid3_matroska extends getid3_handler
case 'A_MPEG/L3': case 'A_MPEG/L3':
case 'A_MPEG/L2': case 'A_MPEG/L2':
case 'A_FLAC': case 'A_FLAC':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true);
$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.'.$track_info['dataformat'].'.php"');
break;
}
if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) { if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set'); $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
@ -385,10 +381,6 @@ class getid3_matroska extends getid3_handler
} }
if (!empty($getid3_temp->info['warning'])) { if (!empty($getid3_temp->info['warning'])) {
foreach ($getid3_temp->info['warning'] as $newerror) { foreach ($getid3_temp->info['warning'] as $newerror) {
if ($track_info['dataformat'] == 'mp3' && preg_match('/^Probable truncated file: expecting \d+ bytes of audio data, only found \d+ \(short by \d+ bytes\)$/', $newerror)) {
// LAME/Xing header is probably set, but audio data is chunked into Matroska file and near-impossible to verify if audio stream is complete, so ignore useless warning
continue;
}
$this->warning($class.'() says: ['.$newerror.']'); $this->warning($class.'() says: ['.$newerror.']');
} }
} }
@ -400,7 +392,7 @@ class getid3_matroska extends getid3_handler
case 'A_AAC/MPEG2/LC/SBR': case 'A_AAC/MPEG2/LC/SBR':
case 'A_AAC/MPEG4/LC': case 'A_AAC/MPEG4/LC':
case 'A_AAC/MPEG4/LC/SBR': case 'A_AAC/MPEG4/LC/SBR':
$this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated'); $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
break; break;
case 'A_VORBIS': case 'A_VORBIS':
@ -415,10 +407,7 @@ class getid3_matroska extends getid3_handler
} }
$vorbis_offset -= 1; $vorbis_offset -= 1;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"');
break;
}
// create temp instance // create temp instance
$getid3_temp = new getID3(); $getid3_temp = new getID3();
@ -455,10 +444,7 @@ class getid3_matroska extends getid3_handler
break; break;
case 'A_MS/ACM': case 'A_MS/ACM':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
break;
}
$parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']); $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
foreach ($parsed as $key => $value) { foreach ($parsed as $key => $value) {
@ -515,7 +501,6 @@ class getid3_matroska extends getid3_handler
switch ($top_element['id']) { switch ($top_element['id']) {
case EBML_ID_EBML: case EBML_ID_EBML:
$info['fileformat'] = 'matroska';
$info['matroska']['header']['offset'] = $top_element['offset']; $info['matroska']['header']['offset'] = $top_element['offset'];
$info['matroska']['header']['length'] = $top_element['length']; $info['matroska']['header']['length'] = $top_element['length'];
@ -534,6 +519,7 @@ class getid3_matroska extends getid3_handler
case EBML_ID_DOCTYPE: case EBML_ID_DOCTYPE:
$element_data['data'] = getid3_lib::trimNullByte($element_data['data']); $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
$info['matroska']['doctype'] = $element_data['data']; $info['matroska']['doctype'] = $element_data['data'];
$info['fileformat'] = $element_data['data'];
break; break;
default: default:
@ -1526,8 +1512,8 @@ class getid3_matroska extends getid3_handler
$CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264'; $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264';
$CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4'; $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4';
$CodecIDlist['V_VP8'] = 'vp8'; $CodecIDlist['V_VP8'] = 'vp8';
$CodecIDlist['V_MS/VFW/FOURCC'] = 'riff'; $CodecIDlist['V_MS/VFW/FOURCC'] = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM)
$CodecIDlist['A_MS/ACM'] = 'riff'; $CodecIDlist['A_MS/ACM'] = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
} }
return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid); return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
} }
@ -1762,4 +1748,4 @@ class getid3_matroska extends getid3_handler
return $info; return $info;
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -30,7 +31,7 @@ class getid3_quicktime extends getid3_handler
$info['quicktime']['hinting'] = false; $info['quicktime']['hinting'] = false;
$info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$offset = 0; $offset = 0;
$atomcounter = 0; $atomcounter = 0;
@ -40,15 +41,15 @@ class getid3_quicktime extends getid3_handler
$info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
break; break;
} }
fseek($this->getid3->fp, $offset, SEEK_SET); $this->fseek($offset);
$AtomHeader = fread($this->getid3->fp, 8); $AtomHeader = $this->fread(8);
$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
$atomname = substr($AtomHeader, 4, 4); $atomname = substr($AtomHeader, 4, 4);
// 64-bit MOV patch by jlegateØktnc*com // 64-bit MOV patch by jlegateØktnc*com
if ($atomsize == 1) { if ($atomsize == 1) {
$atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8)); $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
} }
$info['quicktime'][$atomname]['name'] = $atomname; $info['quicktime'][$atomname]['name'] = $atomname;
@ -66,58 +67,8 @@ class getid3_quicktime extends getid3_handler
// to read user data atoms, you should allow for the terminating 0. // to read user data atoms, you should allow for the terminating 0.
break; break;
} }
switch ($atomname) { $atomHierarchy = array();
case 'mdat': // Media DATa atom $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, round($this->getid3->memory_limit / 2))), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
// 'mdat' contains the actual data for the audio/video
if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
$info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8;
$OldAVDataEnd = $info['avdataend'];
$info['avdataend'] = $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp);
if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode(fread($this->getid3->fp, 4)))) {
$getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
if (!empty($getid3_temp->info['warning'])) {
foreach ($getid3_temp->info['warning'] as $value) {
$info['warning'][] = $value;
}
}
if (!empty($getid3_temp->info['mpeg'])) {
$info['mpeg'] = $getid3_temp->info['mpeg'];
if (isset($info['mpeg']['audio'])) {
$info['audio']['dataformat'] = 'mp3';
$info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
$info['audio']['channels'] = $info['mpeg']['audio']['channels'];
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
$info['bitrate'] = $info['audio']['bitrate'];
}
}
}
unset($getid3_mp3, $getid3_temp);
$info['avdataend'] = $OldAVDataEnd;
unset($OldAVDataEnd);
}
break;
case 'free': // FREE space atom
case 'skip': // SKIP atom
case 'wide': // 64-bit expansion placeholder atom
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
break;
default:
$atomHierarchy = array();
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
break;
}
$offset += $atomsize; $offset += $atomsize;
$atomcounter++; $atomcounter++;
@ -172,15 +123,12 @@ class getid3_quicktime extends getid3_handler
$info = &$this->getid3->info; $info = &$this->getid3->info;
//$atom_parent = array_pop($atomHierarchy); $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
$atom_parent = end($atomHierarchy); // http://www.getid3.org/phpBB3/viewtopic.php?t=1717
array_push($atomHierarchy, $atomname); array_push($atomHierarchy, $atomname);
$atom_structure['hierarchy'] = implode(' ', $atomHierarchy); $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
$atom_structure['name'] = $atomname; $atom_structure['name'] = $atomname;
$atom_structure['size'] = $atomsize; $atom_structure['size'] = $atomsize;
$atom_structure['offset'] = $baseoffset; $atom_structure['offset'] = $baseoffset;
//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8)).'<br>';
//echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8), false).'<br><br>';
switch ($atomname) { switch ($atomname) {
case 'moov': // MOVie container atom case 'moov': // MOVie container atom
case 'trak': // TRAcK container atom case 'trak': // TRAcK container atom
@ -200,27 +148,27 @@ class getid3_quicktime extends getid3_handler
break; break;
case 'ilst': // Item LiST container atom case 'ilst': // Item LiST container atom
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
// some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
// some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted $allnumericnames = true;
$allnumericnames = true;
foreach ($atom_structure['subatoms'] as $subatomarray) {
if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
$allnumericnames = false;
break;
}
}
if ($allnumericnames) {
$newData = array();
foreach ($atom_structure['subatoms'] as $subatomarray) { foreach ($atom_structure['subatoms'] as $subatomarray) {
foreach ($subatomarray['subatoms'] as $newData_subatomarray) { if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); $allnumericnames = false;
$newData[$subatomarray['name']] = $newData_subatomarray;
break; break;
} }
} }
$atom_structure['data'] = $newData; if ($allnumericnames) {
unset($atom_structure['subatoms']); $newData = array();
foreach ($atom_structure['subatoms'] as $subatomarray) {
foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
$newData[$subatomarray['name']] = $newData_subatomarray;
break;
}
}
$atom_structure['data'] = $newData;
unset($atom_structure['subatoms']);
}
} }
break; break;
@ -308,46 +256,46 @@ class getid3_quicktime extends getid3_handler
case 'geID': case 'geID':
case 'plID': case 'plID':
case 'sfID': // iTunes store country case 'sfID': // iTunes store country
case '©alb': // ALBum case "\xA9".'alb': // ALBum
case '©art': // ARTist case "\xA9".'art': // ARTist
case '©ART': case "\xA9".'ART':
case '©aut': case "\xA9".'aut':
case '©cmt': // CoMmenT case "\xA9".'cmt': // CoMmenT
case '©com': // COMposer case "\xA9".'com': // COMposer
case '©cpy': case "\xA9".'cpy':
case '©day': // content created year case "\xA9".'day': // content created year
case '©dir': case "\xA9".'dir':
case '©ed1': case "\xA9".'ed1':
case '©ed2': case "\xA9".'ed2':
case '©ed3': case "\xA9".'ed3':
case '©ed4': case "\xA9".'ed4':
case '©ed5': case "\xA9".'ed5':
case '©ed6': case "\xA9".'ed6':
case '©ed7': case "\xA9".'ed7':
case '©ed8': case "\xA9".'ed8':
case '©ed9': case "\xA9".'ed9':
case '©enc': case "\xA9".'enc':
case '©fmt': case "\xA9".'fmt':
case '©gen': // GENre case "\xA9".'gen': // GENre
case '©grp': // GRouPing case "\xA9".'grp': // GRouPing
case '©hst': case "\xA9".'hst':
case '©inf': case "\xA9".'inf':
case '©lyr': // LYRics case "\xA9".'lyr': // LYRics
case '©mak': case "\xA9".'mak':
case '©mod': case "\xA9".'mod':
case '©nam': // full NAMe case "\xA9".'nam': // full NAMe
case '©ope': case "\xA9".'ope':
case '©PRD': case "\xA9".'PRD':
case '©prd': case "\xA9".'prd':
case '©prf': case "\xA9".'prf':
case '©req': case "\xA9".'req':
case '©src': case "\xA9".'src':
case '©swr': case "\xA9".'swr':
case '©too': // encoder case "\xA9".'too': // encoder
case '©trk': // TRacK case "\xA9".'trk': // TRacK
case '©url': case "\xA9".'url':
case '©wrn': case "\xA9".'wrn':
case '©wrt': // WRiTer case "\xA9".'wrt': // WRiTer
case '----': // itunes specific case '----': // itunes specific
if ($atom_parent == 'udta') { if ($atom_parent == 'udta') {
// User data atom handler // User data atom handler
@ -370,7 +318,7 @@ class getid3_quicktime extends getid3_handler
$boxsmalltype = substr($atom_data, $atomoffset + 2, 2); $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
$boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize); $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
if ($boxsmallsize <= 1) { if ($boxsmallsize <= 1) {
$info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.$atomname.'" at offset: '.($atom_structure['offset'] + $atomoffset); $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
$atom_structure['data'] = null; $atom_structure['data'] = null;
$atomoffset = strlen($atom_data); $atomoffset = strlen($atom_data);
break; break;
@ -380,7 +328,7 @@ class getid3_quicktime extends getid3_handler
$atom_structure['data'] = $boxsmalldata; $atom_structure['data'] = $boxsmalldata;
break; break;
default: default:
$info['warning'][] = 'Unknown QuickTime smallbox type: "'.getid3_lib::PrintHexBytes($boxsmalltype).'" at offset '.$baseoffset; $info['warning'][] = 'Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset;
$atom_structure['data'] = $atom_data; $atom_structure['data'] = $atom_data;
break; break;
} }
@ -392,7 +340,7 @@ class getid3_quicktime extends getid3_handler
$boxtype = substr($atom_data, $atomoffset + 4, 4); $boxtype = substr($atom_data, $atomoffset + 4, 4);
$boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
if ($boxsize <= 1) { if ($boxsize <= 1) {
$info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.$atomname.'" at offset: '.($atom_structure['offset'] + $atomoffset); $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
$atom_structure['data'] = null; $atom_structure['data'] = null;
$atomoffset = strlen($atom_data); $atomoffset = strlen($atom_data);
break; break;
@ -409,7 +357,7 @@ class getid3_quicktime extends getid3_handler
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
switch ($atom_structure['flags_raw']) { switch ($atom_structure['flags_raw']) {
case 0: // data flag case 0: // data flag
case 21: // tmpo/cpil flag case 21: // tmpo/cpil flag
switch ($atomname) { switch ($atomname) {
case 'cpil': case 'cpil':
@ -460,17 +408,27 @@ class getid3_quicktime extends getid3_handler
} }
break; break;
case 1: // text flag case 1: // text flag
case 13: // image flag case 13: // image flag
default: default:
$atom_structure['data'] = substr($boxdata, 8); $atom_structure['data'] = substr($boxdata, 8);
if ($atomname == 'covr') {
// not a foolproof check, but better than nothing
if (preg_match('#^\xFF\xD8\xFF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/jpeg';
} elseif (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/png';
} elseif (preg_match('#^GIF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/gif';
}
}
break; break;
} }
break; break;
default: default:
$info['warning'][] = 'Unknown QuickTime box type: "'.getid3_lib::PrintHexBytes($boxtype).'" at offset '.$baseoffset; $info['warning'][] = 'Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset;
$atom_structure['data'] = $atom_data; $atom_structure['data'] = $atom_data;
} }
@ -840,7 +798,12 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
$sttsEntriesDataOffset = 8; $sttsEntriesDataOffset = 8;
//$FrameRateCalculatorArray = array(); //$FrameRateCalculatorArray = array();
$frames_count = 0; $frames_count = 0;
for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
$max_stts_entries_to_scan = min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']);
if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
$info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).';
}
for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
$atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
$sttsEntriesDataOffset += 4; $sttsEntriesDataOffset += 4;
$atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
@ -1086,8 +1049,8 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
case 'sync': // SYNChronization atom case 'sync': // SYNChronization atom
case 'scpt': // tranSCriPT atom case 'scpt': // tranSCriPT atom
case 'ssrc': // non-primary SouRCe atom case 'ssrc': // non-primary SouRCe atom
for ($i = 0; $i < (strlen($atom_data) % 4); $i++) { for ($i = 0; $i < strlen($atom_data); $i += 4) {
$atom_structure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $i * 4, 4)); @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
} }
break; break;
@ -1260,10 +1223,76 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
break; break;
case 'mdat': // Media DATa atom case 'mdat': // Media DATa atom
// 'mdat' contains the actual data for the audio/video, possibly also subtitles
/* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */
// first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
$mdat_offset = 0;
while (true) {
if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
$mdat_offset += 8;
} elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
$mdat_offset += 8;
} else {
break;
}
}
// check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
while (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
&& ($chapter_string_length < 1000)
&& ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
&& preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) {
$mdat_offset += (2 + $chapter_string_length);
@$info['quicktime']['comments']['chapters'][] = $chapter_matches[0];
}
if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
$info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8;
$OldAVDataEnd = $info['avdataend'];
$info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp);
if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
$getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
if (!empty($getid3_temp->info['warning'])) {
foreach ($getid3_temp->info['warning'] as $value) {
$info['warning'][] = $value;
}
}
if (!empty($getid3_temp->info['mpeg'])) {
$info['mpeg'] = $getid3_temp->info['mpeg'];
if (isset($info['mpeg']['audio'])) {
$info['audio']['dataformat'] = 'mp3';
$info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
$info['audio']['channels'] = $info['mpeg']['audio']['channels'];
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
$info['bitrate'] = $info['audio']['bitrate'];
}
}
}
unset($getid3_mp3, $getid3_temp);
$info['avdataend'] = $OldAVDataEnd;
unset($OldAVDataEnd);
}
unset($mdat_offset, $chapter_string_length, $chapter_matches);
break;
case 'free': // FREE space atom case 'free': // FREE space atom
case 'skip': // SKIP atom case 'skip': // SKIP atom
case 'wide': // 64-bit expansion placeholder atom case 'wide': // 64-bit expansion placeholder atom
// 'mdat' data is too big to deal with, contains no useful metadata
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
// When writing QuickTime files, it is sometimes necessary to update an atom's size. // When writing QuickTime files, it is sometimes necessary to update an atom's size.
@ -1329,7 +1358,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
//$atom_structure['data'] = $atom_data; //$atom_structure['data'] = $atom_data;
break; break;
case '©xyz': // GPS latitude+longitude+altitude case "\xA9".'xyz': // GPS latitude+longitude+altitude
$atom_structure['data'] = $atom_data; $atom_structure['data'] = $atom_data;
if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
@list($all, $latitude, $longitude, $altitude) = $matches; @list($all, $latitude, $longitude, $altitude) = $matches;
@ -1358,16 +1387,12 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
} }
break; break;
case 'NCHD': // MakerNoteVersion case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
$atom_structure['data'] = $atom_data;
break;
case 'NCTG': // NikonTags
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
$atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
break; break;
case 'NCDB': // NikonTags case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
$atom_structure['data'] = $atom_data; $atom_structure['data'] = $atom_data;
break; break;
@ -1391,7 +1416,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
break; break;
default: default:
$info['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset; $info['warning'][] = 'Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset;
$atom_structure['data'] = $atom_data; $atom_structure['data'] = $atom_data;
break; break;
} }
@ -2086,58 +2111,58 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') { public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
static $handyatomtranslatorarray = array(); static $handyatomtranslatorarray = array();
if (empty($handyatomtranslatorarray)) { if (empty($handyatomtranslatorarray)) {
$handyatomtranslatorarray['©cpy'] = 'copyright'; $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
$handyatomtranslatorarray['©day'] = 'creation_date'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
$handyatomtranslatorarray['©dir'] = 'director'; $handyatomtranslatorarray["\xA9".'dir'] = 'director';
$handyatomtranslatorarray['©ed1'] = 'edit1'; $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
$handyatomtranslatorarray['©ed2'] = 'edit2'; $handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
$handyatomtranslatorarray['©ed3'] = 'edit3'; $handyatomtranslatorarray["\xA9".'ed3'] = 'edit3';
$handyatomtranslatorarray['©ed4'] = 'edit4'; $handyatomtranslatorarray["\xA9".'ed4'] = 'edit4';
$handyatomtranslatorarray['©ed5'] = 'edit5'; $handyatomtranslatorarray["\xA9".'ed5'] = 'edit5';
$handyatomtranslatorarray['©ed6'] = 'edit6'; $handyatomtranslatorarray["\xA9".'ed6'] = 'edit6';
$handyatomtranslatorarray['©ed7'] = 'edit7'; $handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
$handyatomtranslatorarray['©ed8'] = 'edit8'; $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
$handyatomtranslatorarray['©ed9'] = 'edit9'; $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
$handyatomtranslatorarray['©fmt'] = 'format'; $handyatomtranslatorarray["\xA9".'fmt'] = 'format';
$handyatomtranslatorarray['©inf'] = 'information'; $handyatomtranslatorarray["\xA9".'inf'] = 'information';
$handyatomtranslatorarray['©prd'] = 'producer'; $handyatomtranslatorarray["\xA9".'prd'] = 'producer';
$handyatomtranslatorarray['©prf'] = 'performers'; $handyatomtranslatorarray["\xA9".'prf'] = 'performers';
$handyatomtranslatorarray['©req'] = 'system_requirements'; $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
$handyatomtranslatorarray['©src'] = 'source_credit'; $handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
$handyatomtranslatorarray['©wrt'] = 'writer'; $handyatomtranslatorarray["\xA9".'wrt'] = 'writer';
// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
$handyatomtranslatorarray['©nam'] = 'title'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
$handyatomtranslatorarray['©cmt'] = 'comment'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
$handyatomtranslatorarray['©wrn'] = 'warning'; $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
$handyatomtranslatorarray['©hst'] = 'host_computer'; $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
$handyatomtranslatorarray['©mak'] = 'make'; $handyatomtranslatorarray["\xA9".'mak'] = 'make';
$handyatomtranslatorarray['©mod'] = 'model'; $handyatomtranslatorarray["\xA9".'mod'] = 'model';
$handyatomtranslatorarray['©PRD'] = 'product'; $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
$handyatomtranslatorarray['©swr'] = 'software'; $handyatomtranslatorarray["\xA9".'swr'] = 'software';
$handyatomtranslatorarray['©aut'] = 'author'; $handyatomtranslatorarray["\xA9".'aut'] = 'author';
$handyatomtranslatorarray['©ART'] = 'artist'; $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
$handyatomtranslatorarray['©trk'] = 'track'; $handyatomtranslatorarray["\xA9".'trk'] = 'track';
$handyatomtranslatorarray['©alb'] = 'album'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
$handyatomtranslatorarray['©com'] = 'comment'; $handyatomtranslatorarray["\xA9".'com'] = 'comment';
$handyatomtranslatorarray['©gen'] = 'genre'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray['©ope'] = 'composer'; $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
$handyatomtranslatorarray['©url'] = 'url'; $handyatomtranslatorarray["\xA9".'url'] = 'url';
$handyatomtranslatorarray['©enc'] = 'encoder'; $handyatomtranslatorarray["\xA9".'enc'] = 'encoder';
// http://atomicparsley.sourceforge.net/mpeg-4files.html // http://atomicparsley.sourceforge.net/mpeg-4files.html
$handyatomtranslatorarray['©art'] = 'artist'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
$handyatomtranslatorarray['aART'] = 'album_artist'; $handyatomtranslatorarray['aART'] = 'album_artist';
$handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0 $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
$handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0 $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
$handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0 $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray['©too'] = 'encoder'; // iTunes 4.0 $handyatomtranslatorarray["\xA9".'too'] = 'encoder'; // iTunes 4.0
$handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0 $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
$handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0? $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
$handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0 $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
$handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0 $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
$handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0 $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
$handyatomtranslatorarray['©grp'] = 'grouping'; // iTunes 4.2 $handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
$handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9 $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
$handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9 $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
$handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9 $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
@ -2145,7 +2170,7 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
$handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9 $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
$handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9 $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
$handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0 $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
$handyatomtranslatorarray['©lyr'] = 'lyrics'; // iTunes 5.0 $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
$handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0 $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0 $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0 $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
@ -2218,4 +2243,4 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
return substr($pascalstring, 1); return substr($pascalstring, 1);
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -26,8 +27,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE_
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
class getid3_riff extends getid3_handler class getid3_riff extends getid3_handler {
{
protected $container = 'riff'; // default
public function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -58,7 +60,8 @@ class getid3_riff extends getid3_handler
switch ($RIFFtype) { switch ($RIFFtype) {
case 'FORM': // AIFF, AIFC case 'FORM': // AIFF, AIFC
$info['fileformat'] = 'aiff'; //$info['fileformat'] = 'aiff';
$this->container = 'aiff';
$thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
$thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4)); $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
break; break;
@ -66,13 +69,18 @@ class getid3_riff extends getid3_handler
case 'RIFF': // AVI, WAV, etc case 'RIFF': // AVI, WAV, etc
case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
$info['fileformat'] = 'riff'; //$info['fileformat'] = 'riff';
$this->container = 'riff';
$thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
if ($RIFFsubtype == 'RMP3') { if ($RIFFsubtype == 'RMP3') {
// RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
$RIFFsubtype = 'WAVE'; $RIFFsubtype = 'WAVE';
} }
$thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4)); if ($RIFFsubtype != 'AMV ') {
// AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
// Handled separately in ParseRIFFAMV()
$thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
}
if (($info['avdataend'] - $info['filesize']) == 1) { if (($info['avdataend'] - $info['filesize']) == 1) {
// LiteWave appears to incorrectly *not* pad actual output file // LiteWave appears to incorrectly *not* pad actual output file
// to nearest WORD boundary so may appear to be short by one // to nearest WORD boundary so may appear to be short by one
@ -110,16 +118,19 @@ class getid3_riff extends getid3_handler
$nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size']; $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
switch ($nextRIFFheaderID) { switch ($nextRIFFheaderID) {
case 'RIFF': case 'RIFF':
$chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset); $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
if (!isset($thisfile_riff[$nextRIFFtype])) { if (!isset($thisfile_riff[$nextRIFFtype])) {
$thisfile_riff[$nextRIFFtype] = array(); $thisfile_riff[$nextRIFFtype] = array();
} }
$thisfile_riff[$nextRIFFtype][] = $chunkdata; $thisfile_riff[$nextRIFFtype][] = $chunkdata;
break; break;
case 'AMV ':
unset($info['riff']);
$info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
break;
case 'JUNK': case 'JUNK':
// ignore // ignore
$thisfile_riff[$nextRIFFheaderID][] = $chunkdata; $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
@ -152,13 +163,17 @@ class getid3_riff extends getid3_handler
default: default:
$this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'); $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
unset($info['fileformat']); //unset($info['fileformat']);
return false; return false;
} }
$streamindex = 0; $streamindex = 0;
switch ($RIFFsubtype) { switch ($RIFFsubtype) {
// http://en.wikipedia.org/wiki/Wav
case 'WAVE': case 'WAVE':
$info['fileformat'] = 'wav';
if (empty($thisfile_audio['bitrate_mode'])) { if (empty($thisfile_audio['bitrate_mode'])) {
$thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio['bitrate_mode'] = 'cbr';
} }
@ -588,10 +603,13 @@ class getid3_riff extends getid3_handler
} }
break; break;
// http://en.wikipedia.org/wiki/Audio_Video_Interleave
case 'AVI ': case 'AVI ':
$info['fileformat'] = 'avi';
$info['mime_type'] = 'video/avi';
$thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
$thisfile_video['dataformat'] = 'avi'; $thisfile_video['dataformat'] = 'avi';
$info['mime_type'] = 'video/avi';
if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
@ -825,7 +843,7 @@ class getid3_riff extends getid3_handler
switch ($strhfccType) { switch ($strhfccType) {
case 'vids': case 'vids':
$thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($info['fileformat'] == 'riff')); $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
$thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']; $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
if ($thisfile_riff_video_current['codec'] == 'DV') { if ($thisfile_riff_video_current['codec'] == 'DV') {
@ -875,11 +893,28 @@ class getid3_riff extends getid3_handler
} }
break; break;
case 'AMV ':
$info['fileformat'] = 'amv';
$info['mime_type'] = 'video/amv';
$thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
$thisfile_video['dataformat'] = 'mjpeg';
$thisfile_video['codec'] = 'mjpeg';
$thisfile_video['lossless'] = false;
$thisfile_video['bits_per_sample'] = 24;
$thisfile_audio['dataformat'] = 'adpcm';
$thisfile_audio['lossless'] = false;
break;
// http://en.wikipedia.org/wiki/CD-DA
case 'CDDA': case 'CDDA':
$thisfile_audio['bitrate_mode'] = 'cbr'; $info['fileformat'] = 'cda';
unset($info['mime_type']);
$thisfile_audio_dataformat = 'cda'; $thisfile_audio_dataformat = 'cda';
$thisfile_audio['lossless'] = true;
unset($info['mime_type']);
$info['avdataoffset'] = 44; $info['avdataoffset'] = 44;
@ -901,6 +936,7 @@ class getid3_riff extends getid3_handler
$info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
// hardcoded data for CD-audio // hardcoded data for CD-audio
$thisfile_audio['lossless'] = true;
$thisfile_audio['sample_rate'] = 44100; $thisfile_audio['sample_rate'] = 44100;
$thisfile_audio['channels'] = 2; $thisfile_audio['channels'] = 2;
$thisfile_audio['bits_per_sample'] = 16; $thisfile_audio['bits_per_sample'] = 16;
@ -909,13 +945,15 @@ class getid3_riff extends getid3_handler
} }
break; break;
// http://en.wikipedia.org/wiki/AIFF
case 'AIFF': case 'AIFF':
case 'AIFC': case 'AIFC':
$info['fileformat'] = 'aiff';
$info['mime_type'] = 'audio/x-aiff';
$thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio['bitrate_mode'] = 'cbr';
$thisfile_audio_dataformat = 'aiff'; $thisfile_audio_dataformat = 'aiff';
$thisfile_audio['lossless'] = true; $thisfile_audio['lossless'] = true;
$info['mime_type'] = 'audio/x-aiff';
if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
@ -1028,12 +1066,15 @@ class getid3_riff extends getid3_handler
*/ */
break; break;
// http://en.wikipedia.org/wiki/8SVX
case '8SVX': case '8SVX':
$info['fileformat'] = '8svx';
$info['mime_type'] = 'audio/8svx';
$thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio['bitrate_mode'] = 'cbr';
$thisfile_audio_dataformat = '8svx'; $thisfile_audio_dataformat = '8svx';
$thisfile_audio['bits_per_sample'] = 8; $thisfile_audio['bits_per_sample'] = 8;
$thisfile_audio['channels'] = 1; // overridden below, if need be $thisfile_audio['channels'] = 1; // overridden below, if need be
$info['mime_type'] = 'audio/x-aiff';
if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
@ -1108,31 +1149,31 @@ class getid3_riff extends getid3_handler
} }
break; break;
case 'CDXA': case 'CDXA':
$info['mime_type'] = 'video/mpeg'; $info['fileformat'] = 'vcd'; // Asume Video CD
$info['mime_type'] = 'video/mpeg';
if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp = new getID3();
$getid3_mpeg = new getid3_mpeg($getid3_temp); $getid3_temp->openfile($this->getid3->filename);
$getid3_mpeg->Analyze(); $getid3_mpeg = new getid3_mpeg($getid3_temp);
if (empty($getid3_temp->info['error'])) { $getid3_mpeg->Analyze();
$info['audio'] = $getid3_temp->info['audio']; if (empty($getid3_temp->info['error'])) {
$info['video'] = $getid3_temp->info['video']; $info['audio'] = $getid3_temp->info['audio'];
$info['mpeg'] = $getid3_temp->info['mpeg']; $info['video'] = $getid3_temp->info['video'];
$info['warning'] = $getid3_temp->info['warning']; $info['mpeg'] = $getid3_temp->info['mpeg'];
} $info['warning'] = $getid3_temp->info['warning'];
unset($getid3_temp, $getid3_mpeg);
} }
unset($getid3_temp, $getid3_mpeg);
} }
break; break;
default: default:
$info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
unset($info['fileformat']); //unset($info['fileformat']);
break;
} }
switch ($RIFFsubtype) { switch ($RIFFsubtype) {
@ -1150,6 +1191,7 @@ class getid3_riff extends getid3_handler
if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) { if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_id3v2 = new getid3_id3v2($getid3_temp); $getid3_id3v2 = new getid3_id3v2($getid3_temp);
@ -1278,6 +1320,115 @@ class getid3_riff extends getid3_handler
return true; return true;
} }
public function ParseRIFFAMV($startoffset, $maxoffset) {
// AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
// https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
//typedef struct _amvmainheader {
//FOURCC fcc; // 'amvh'
//DWORD cb;
//DWORD dwMicroSecPerFrame;
//BYTE reserve[28];
//DWORD dwWidth;
//DWORD dwHeight;
//DWORD dwSpeed;
//DWORD reserve0;
//DWORD reserve1;
//BYTE bTimeSec;
//BYTE bTimeMin;
//WORD wTimeHour;
//} AMVMAINHEADER;
$info = &$this->getid3->info;
$RIFFchunk = false;
try {
$this->fseek($startoffset);
$maxoffset = min($maxoffset, $info['avdataend']);
$AMVheader = $this->fread(284);
if (substr($AMVheader, 0, 8) != 'hdrlamvh') {
throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"');
}
if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"');
}
$RIFFchunk = array();
$RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4));
$RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved?
$RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4));
$RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4));
$RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4));
$RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved?
$RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved?
$RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1));
$RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1));
$RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2));
$info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
$info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
$info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
$info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
// the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded
if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"');
}
// followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144
if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") {
throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"');
}
// followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
}
// followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") {
throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"');
}
// followed by 20 bytes of a modified WAVEFORMATEX:
// typedef struct {
// WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code)
// WORD nChannels; //(Fixme: this is always 1)
// DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050)
// DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
// WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?)
// WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
// WORD cbSize; //(Fixme: this seems to be 0 in AMV files)
// WORD reserved;
// } WAVEFORMATEX;
$RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2));
$RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2));
$RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4));
$RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4));
$RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2));
$RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2));
$RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2));
$RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2));
$info['audio']['lossless'] = false;
$info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec'];
$info['audio']['channels'] = $RIFFchunk['strf']['nchannels'];
$info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
$info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
$info['audio']['bitrate_mode'] = 'cbr';
} catch (getid3_exception $e) {
if ($e->getCode() == 10) {
$this->warning('RIFFAMV parser: '.$e->getMessage());
} else {
throw $e;
}
}
return $RIFFchunk;
}
public function ParseRIFF($startoffset, $maxoffset) { public function ParseRIFF($startoffset, $maxoffset) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -1329,7 +1480,7 @@ class getid3_riff extends getid3_handler
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info['avdataoffset'] = $this->ftell() - 4; $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
$getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
$getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
$getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
if (isset($getid3_temp->info['mpeg']['audio'])) { if (isset($getid3_temp->info['mpeg']['audio'])) {
$info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
@ -1412,7 +1563,7 @@ class getid3_riff extends getid3_handler
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
$getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false); $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
if (empty($getid3_temp->info['error'])) { if (empty($getid3_temp->info['error'])) {
$info['audio'] = $getid3_temp->info['audio']; $info['audio'] = $getid3_temp->info['audio'];
@ -2426,10 +2577,10 @@ class getid3_riff extends getid3_handler
} }
private function EitherEndian2Int($byteword, $signed=false) { private function EitherEndian2Int($byteword, $signed=false) {
if ($this->getid3->info['fileformat'] == 'riff') { if ($this->container == 'riff') {
return getid3_lib::LittleEndian2Int($byteword, $signed); return getid3_lib::LittleEndian2Int($byteword, $signed);
} }
return getid3_lib::BigEndian2Int($byteword, false, $signed); return getid3_lib::BigEndian2Int($byteword, false, $signed);
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -470,4 +471,4 @@ class getid3_ac3 extends getid3_handler
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -287,4 +288,4 @@ class getid3_dts extends getid3_handler
return false; return false;
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -439,4 +440,4 @@ class getid3_flac extends getid3_handler
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved'); return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -95,8 +96,8 @@ class getid3_mp3 extends getid3_handler
// Not sure what version of LAME this is - look in padding of last frame for longer version string // Not sure what version of LAME this is - look in padding of last frame for longer version string
$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
fseek($this->getid3->fp, $PossibleLAMEversionStringOffset); $this->fseek($PossibleLAMEversionStringOffset);
$PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength); $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
switch (substr($CurrentDataLAMEversionString, -1)) { switch (substr($CurrentDataLAMEversionString, -1)) {
case 'a': case 'a':
case 'b': case 'b':
@ -422,12 +423,12 @@ class getid3_mp3 extends getid3_handler
$MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
} }
if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) { if ($this->fseek($offset) != 0) {
$info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset; $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
return false; return false;
} }
//$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
$headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
// MP3 audio frame structure: // MP3 audio frame structure:
// $aa $aa $aa $aa [$bb $bb] $cc... // $aa $aa $aa $aa [$bb $bb] $cc...
@ -890,19 +891,21 @@ class getid3_mp3 extends getid3_handler
if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) { if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) { if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) { if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
// ignore, audio data is broken into chunks so will always be data "missing" // ignore, audio data is broken into chunks so will always be data "missing"
} elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) { }
$info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
} else { $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
$info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)'; }
else {
$this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
} }
} else { } else {
if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
// $prenullbytefileoffset = ftell($this->getid3->fp); // $prenullbytefileoffset = $this->ftell();
// fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); // $this->fseek($info['avdataend']);
// $PossibleNullByte = fread($this->getid3->fp, 1); // $PossibleNullByte = $this->fread(1);
// fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET); // $this->fseek($prenullbytefileoffset);
// if ($PossibleNullByte === "\x00") { // if ($PossibleNullByte === "\x00") {
$info['avdataend']--; $info['avdataend']--;
// $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
@ -1118,8 +1121,8 @@ class getid3_mp3 extends getid3_handler
public function FreeFormatFrameLength($offset, $deepscan=false) { public function FreeFormatFrameLength($offset, $deepscan=false) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $offset, SEEK_SET); $this->fseek($offset);
$MPEGaudioData = fread($this->getid3->fp, 32768); $MPEGaudioData = $this->fread(32768);
$SyncPattern1 = substr($MPEGaudioData, 0, 4); $SyncPattern1 = substr($MPEGaudioData, 0, 4);
// may be different pattern due to padding // may be different pattern due to padding
@ -1166,8 +1169,8 @@ class getid3_mp3 extends getid3_handler
$ActualFrameLengthValues = array(); $ActualFrameLengthValues = array();
$nextoffset = $offset + $framelength; $nextoffset = $offset + $framelength;
while ($nextoffset < ($info['avdataend'] - 6)) { while ($nextoffset < ($info['avdataend'] - 6)) {
fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET); $this->fseek($nextoffset - 1);
$NextSyncPattern = fread($this->getid3->fp, 6); $NextSyncPattern = $this->fread(6);
if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
// good - found where expected // good - found where expected
$ActualFrameLengthValues[] = $framelength; $ActualFrameLengthValues[] = $framelength;
@ -1215,22 +1218,22 @@ class getid3_mp3 extends getid3_handler
$Distribution['padding'] = array(); $Distribution['padding'] = array();
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$max_frames_scan = 5000; $max_frames_scan = 5000;
$frames_scanned = 0; $frames_scanned = 0;
$previousvalidframe = $info['avdataoffset']; $previousvalidframe = $info['avdataoffset'];
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
set_time_limit(30); set_time_limit(30);
$head4 = fread($this->getid3->fp, 4); $head4 = $this->fread(4);
if (strlen($head4) < 4) { if (strlen($head4) < 4) {
break; break;
} }
if ($head4{0} != "\xFF") { if ($head4{0} != "\xFF") {
for ($i = 1; $i < 4; $i++) { for ($i = 1; $i < 4; $i++) {
if ($head4{$i} == "\xFF") { if ($head4{$i} == "\xFF") {
fseek($this->getid3->fp, $i - 4, SEEK_CUR); $this->fseek($i - 4, SEEK_CUR);
continue 2; continue 2;
} }
} }
@ -1258,9 +1261,9 @@ class getid3_mp3 extends getid3_handler
$LongMPEGfrequencyLookup[$head4]); $LongMPEGfrequencyLookup[$head4]);
} }
if ($MPEGaudioHeaderLengthCache[$head4] > 4) { if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
$WhereWeWere = ftell($this->getid3->fp); $WhereWeWere = $this->ftell();
fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
$next4 = fread($this->getid3->fp, 4); $next4 = $this->fread(4);
if ($next4{0} == "\xFF") { if ($next4{0} == "\xFF") {
if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4); $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
@ -1269,7 +1272,7 @@ class getid3_mp3 extends getid3_handler
$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
} }
if ($MPEGaudioHeaderValidCache[$next4]) { if ($MPEGaudioHeaderValidCache[$next4]) {
fseek($this->getid3->fp, -4, SEEK_CUR); $this->fseek(-4, SEEK_CUR);
getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]); getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]); getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
@ -1277,7 +1280,7 @@ class getid3_mp3 extends getid3_handler
getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
$pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
$info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
foreach ($Distribution as $key1 => $value1) { foreach ($Distribution as $key1 => $value1) {
foreach ($value1 as $key2 => $value2) { foreach ($value1 as $key2 => $value2) {
@ -1290,7 +1293,7 @@ class getid3_mp3 extends getid3_handler
} }
} }
unset($next4); unset($next4);
fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET); $this->fseek($WhereWeWere - 3);
} }
} }
@ -1355,13 +1358,13 @@ class getid3_mp3 extends getid3_handler
} }
fseek($this->getid3->fp, $avdataoffset, SEEK_SET); $this->fseek($avdataoffset);
$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
if ($sync_seek_buffer_size <= 0) { if ($sync_seek_buffer_size <= 0) {
$info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
return false; return false;
} }
$header = fread($this->getid3->fp, $sync_seek_buffer_size); $header = $this->fread($sync_seek_buffer_size);
$sync_seek_buffer_size = strlen($header); $sync_seek_buffer_size = strlen($header);
$SynchSeekOffset = 0; $SynchSeekOffset = 0;
while ($SynchSeekOffset < $sync_seek_buffer_size) { while ($SynchSeekOffset < $sync_seek_buffer_size) {
@ -1473,7 +1476,7 @@ class getid3_mp3 extends getid3_handler
$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
$synchstartoffset = $info['avdataoffset']; $synchstartoffset = $info['avdataoffset'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
// you can play with these numbers: // you can play with these numbers:
$max_frames_scan = 50000; $max_frames_scan = 50000;
@ -1488,13 +1491,13 @@ class getid3_mp3 extends getid3_handler
$pct_data_scanned = 0; $pct_data_scanned = 0;
for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
$frames_scanned_this_segment = 0; $frames_scanned_this_segment = 0;
if (ftell($this->getid3->fp) >= $info['avdataend']) { if ($this->ftell() >= $info['avdataend']) {
break; break;
} }
$scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments))); $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
if ($current_segment > 0) { if ($current_segment > 0) {
fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET); $this->fseek($scan_start_offset[$current_segment]);
$buffer_4k = fread($this->getid3->fp, 4096); $buffer_4k = $this->fread(4096);
for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) { for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
@ -1523,7 +1526,7 @@ class getid3_mp3 extends getid3_handler
} }
$frames_scanned++; $frames_scanned++;
if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) { if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
$this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']); $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) { if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
// file likely contains < $max_frames_scan, just scan as one segment // file likely contains < $max_frames_scan, just scan as one segment
$max_scan_segments = 1; $max_scan_segments = 1;
@ -2006,4 +2009,4 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org'); return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -114,6 +115,66 @@ class getid3_ogg extends getid3_handler
$info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['bitrate_mode'] = 'vbr';
} }
} elseif (substr($filedata, 0, 7) == "\x80".'theora') {
// http://www.theora.org/doc/Theora.pdf (section 6.2)
$info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7); // hard-coded to "\x80.'theora'
$filedataoffset += 7;
$info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xFC00) >> 10;
$info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x03E0) >> 5;
$info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0018) >> 3;
$info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0007) >> 0; // should be 0
$info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
$info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
$info['video']['dataformat'] = 'theora';
$info['mime_type'] = 'video/ogg';
//$info['audio']['bitrate_mode'] = 'abr';
//$info['audio']['lossless'] = false;
$info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
$info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
$info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
}
if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
$info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
}
$info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable';
} elseif (substr($filedata, 0, 8) == "fishead\x00") { } elseif (substr($filedata, 0, 8) == "fishead\x00") {
@ -172,8 +233,8 @@ class getid3_ogg extends getid3_handler
} elseif (substr($filedata, 1, 6) == 'theora') { } elseif (substr($filedata, 1, 6) == 'theora') {
$info['video']['dataformat'] = 'theora'; $info['video']['dataformat'] = 'theora1';
$info['error'][] = 'Ogg Theora not correctly handled in this version of getID3 ['.$this->getid3->version().']'; $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']';
//break; //break;
} elseif (substr($filedata, 1, 6) == 'vorbis') { } elseif (substr($filedata, 1, 6) == 'vorbis') {
@ -668,4 +729,28 @@ class getid3_ogg extends getid3_handler
return round($qval, 1); // 5 or 4.9 return round($qval, 1); // 5 or 4.9
} }
} public static function TheoraColorSpace($colorspace_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.3)
static $TheoraColorSpaceLookup = array();
if (empty($TheoraColorSpaceLookup)) {
$TheoraColorSpaceLookup[0] = 'Undefined';
$TheoraColorSpaceLookup[1] = 'Rec. 470M';
$TheoraColorSpaceLookup[2] = 'Rec. 470BG';
$TheoraColorSpaceLookup[3] = 'Reserved';
}
return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
}
public static function TheoraPixelFormat($pixelformat_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.4)
static $TheoraPixelFormatLookup = array();
if (empty($TheoraPixelFormatLookup)) {
$TheoraPixelFormatLookup[0] = '4:2:0';
$TheoraPixelFormatLookup[1] = 'Reserved';
$TheoraPixelFormatLookup[2] = '4:2:2';
$TheoraPixelFormatLookup[3] = '4:4:4';
}
return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
}
}

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -32,8 +33,8 @@ class getid3_apetag extends getid3_handler
if ($this->overrideendoffset == 0) { if ($this->overrideendoffset == 0) {
fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
$APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
@ -51,8 +52,8 @@ class getid3_apetag extends getid3_handler
} else { } else {
fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET); $this->fseek($this->overrideendoffset - $apetagheadersize);
if (fread($this->getid3->fp, 8) == 'APETAGEX') { if ($this->fread(8) == 'APETAGEX') {
$info['ape']['tag_offset_end'] = $this->overrideendoffset; $info['ape']['tag_offset_end'] = $this->overrideendoffset;
} }
@ -68,21 +69,21 @@ class getid3_apetag extends getid3_handler
// shortcut // shortcut
$thisfile_ape = &$info['ape']; $thisfile_ape = &$info['ape'];
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
$APEfooterData = fread($this->getid3->fp, 32); $APEfooterData = $this->fread(32);
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
$info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
return false; return false;
} }
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
$thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp); $thisfile_ape['tag_offset_start'] = $this->ftell();
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
} else { } else {
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET); $this->fseek($thisfile_ape['tag_offset_start']);
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
} }
$info['avdataend'] = $thisfile_ape['tag_offset_start']; $info['avdataend'] = $thisfile_ape['tag_offset_start'];
@ -367,4 +368,4 @@ class getid3_apetag extends getid3_handler
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -25,9 +26,9 @@ class getid3_id3v1 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, -256, SEEK_END); $this->fseek(-256, SEEK_END);
$preid3v1 = fread($this->getid3->fp, 128); $preid3v1 = $this->fread(128);
$id3v1tag = fread($this->getid3->fp, 128); $id3v1tag = $this->fread(128);
if (substr($id3v1tag, 0, 3) == 'TAG') { if (substr($id3v1tag, 0, 3) == 'TAG') {
@ -356,4 +357,4 @@ class getid3_id3v1 extends getid3_handler
return $ID3v1Tag; return $ID3v1Tag;
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -51,8 +52,8 @@ class getid3_id3v2 extends getid3_handler
$thisfile_id3v2_flags = &$thisfile_id3v2['flags']; $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET); $this->fseek($this->StartingOffset);
$header = fread($this->getid3->fp, 10); $header = $this->fread(10);
if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
$thisfile_id3v2['majorversion'] = ord($header{3}); $thisfile_id3v2['majorversion'] = ord($header{3});
@ -131,7 +132,7 @@ class getid3_id3v2 extends getid3_handler
} }
if ($sizeofframes > 0) { if ($sizeofframes > 0) {
$framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
// if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
@ -423,7 +424,7 @@ class getid3_id3v2 extends getid3_handler
// ID3v2 size 4 * %0xxxxxxx // ID3v2 size 4 * %0xxxxxxx
if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
$footer = fread($this->getid3->fp, 10); $footer = $this->fread(10);
if (substr($footer, 0, 3) == '3DI') { if (substr($footer, 0, 3) == '3DI') {
$thisfile_id3v2['footer'] = true; $thisfile_id3v2['footer'] = true;
$thisfile_id3v2['majorversion_footer'] = ord($footer{3}); $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
@ -642,7 +643,12 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
} else {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
}
} }
//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
@ -1077,7 +1083,12 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = $frame_text; $parsedFrame['data'] = $frame_text;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
} else {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
} }
} }
@ -1885,7 +1896,7 @@ class getid3_id3v2 extends getid3_handler
$frame_offset += 2; $frame_offset += 2;
$parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
for ($i = 0; $i < $frame_indexpoints; $i++) { for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
$parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
$frame_offset += $frame_bytesperpoint; $frame_offset += $frame_bytesperpoint;
} }
@ -3411,4 +3422,3 @@ class getid3_id3v2 extends getid3_handler
} }
} }

View File

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -27,8 +28,8 @@ class getid3_lyrics3 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] $this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
$lyrics3_id3v1 = fread($this->getid3->fp, 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
@ -68,9 +69,9 @@ class getid3_lyrics3 extends getid3_handler
if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) { if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
fseek($this->getid3->fp, $info['ape']['tag_offset_start'] - 15, SEEK_SET); $this->fseek($info['ape']['tag_offset_start'] - 15);
$lyrics3lsz = fread($this->getid3->fp, 6); $lyrics3lsz = $this->fread(6);
$lyrics3end = fread($this->getid3->fp, 9); $lyrics3end = $this->fread(9);
if ($lyrics3end == 'LYRICSEND') { if ($lyrics3end == 'LYRICSEND') {
// Lyrics3v1, APE, maybe ID3v1 // Lyrics3v1, APE, maybe ID3v1
@ -101,20 +102,19 @@ class getid3_lyrics3 extends getid3_handler
if (!isset($info['ape'])) { if (!isset($info['ape'])) {
$GETID3_ERRORARRAY = &$info['warning']; $GETID3_ERRORARRAY = &$info['warning'];
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_apetag = new getid3_apetag($getid3_temp); $getid3_apetag = new getid3_apetag($getid3_temp);
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
$getid3_apetag->Analyze(); $getid3_apetag->Analyze();
if (!empty($getid3_temp->info['ape'])) { if (!empty($getid3_temp->info['ape'])) {
$info['ape'] = $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);
} }
if (!empty($getid3_temp->info['replay_gain'])) {
$info['replay_gain'] = $getid3_temp->info['replay_gain'];
}
unset($getid3_temp, $getid3_apetag);
} }
} }
@ -132,11 +132,11 @@ class getid3_lyrics3 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, $endoffset, SEEK_SET); $this->fseek($endoffset);
if ($length <= 0) { if ($length <= 0) {
return false; return false;
} }
$rawdata = fread($this->getid3->fp, $length); $rawdata = $this->fread($length);
$ParsedLyrics3['raw']['lyrics3version'] = $version; $ParsedLyrics3['raw']['lyrics3version'] = $version;
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length; $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
@ -169,7 +169,7 @@ class getid3_lyrics3 extends getid3_handler
$ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3); $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
} else { } else {
$info['error'][] = '"LYRICSEND" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; $info['error'][] = '"LYRICSEND" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
return false; return false;
} }
break; break;
@ -217,7 +217,7 @@ class getid3_lyrics3 extends getid3_handler
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3); $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
} }
} else { } else {
$info['error'][] = '"LYRICS200" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; $info['error'][] = '"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
return false; return false;
} }
break; break;
@ -291,4 +291,4 @@ class getid3_lyrics3 extends getid3_handler
} }
return null; return null;
} }
} }

View File

@ -2,6 +2,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
***************************************************************** *****************************************************************
@ -67,13 +68,13 @@ What does getID3() do?
=========================================================================== ===========================================================================
Reads & parses (to varying degrees): Reads & parses (to varying degrees):
# tags: ¤ tags:
* APE (v1 and v2) * APE (v1 and v2)
* ID3v1 (& ID3v1.1) * ID3v1 (& ID3v1.1)
* ID3v2 (v2.4, v2.3, v2.2) * ID3v2 (v2.4, v2.3, v2.2)
* Lyrics3 (v1 & v2) * Lyrics3 (v1 & v2)
# audio-lossy: ¤ audio-lossy:
* MP3/MP2/MP1 * MP3/MP2/MP1
* MPC / Musepack * MPC / Musepack
* Ogg (Vorbis, OggFLAC, Speex) * Ogg (Vorbis, OggFLAC, Speex)
@ -85,7 +86,7 @@ Reads & parses (to varying degrees):
* DSS * DSS
* VQF * VQF
# audio-lossless: ¤ audio-lossless:
* AIFF * AIFF
* AU * AU
* Bonk * Bonk
@ -104,7 +105,7 @@ Reads & parses (to varying degrees):
* WAV (RIFF) * WAV (RIFF)
* WavPack * WavPack
# audio-video: ¤ audio-video:
* ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV) * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
* AVI (RIFF) * AVI (RIFF)
* Flash * Flash
@ -114,7 +115,7 @@ Reads & parses (to varying degrees):
* Quicktime (including MP4) * Quicktime (including MP4)
* RealVideo * RealVideo
# still image: ¤ still image:
* BMP * BMP
* GIF * GIF
* JPEG * JPEG
@ -123,7 +124,7 @@ Reads & parses (to varying degrees):
* SWF (Flash) * SWF (Flash)
* PhotoCD * PhotoCD
# data: ¤ data:
* ISO-9660 CD-ROM image (directory structure) * ISO-9660 CD-ROM image (directory structure)
* SZIP (limited support) * SZIP (limited support)
* ZIP (directory structure) * ZIP (directory structure)
@ -309,7 +310,7 @@ http://www.getid3.org/phpBB3/viewforum.php?f=7
(http://web.inter.nl.net/users/hvdh/lossless/lossless.htm) (http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
* Support for RIFF-INFO chunks * Support for RIFF-INFO chunks
* http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html * http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
(thanks Nick Humfrey <njh@surgeradio*co*uk>) (thanks Nick Humfrey <njhØsurgeradio*co*uk>)
* http://abcavi.narod.ru/sof/abcavi/infotags.htm * http://abcavi.narod.ru/sof/abcavi/infotags.htm
(thanks Kibi) (thanks Kibi)
* Better support for Bink video * Better support for Bink video
@ -324,23 +325,23 @@ http://www.getid3.org/phpBB3/viewforum.php?f=7
* Support for IFF * Support for IFF
* Support for ICO * Support for ICO
* Support for ANI * Support for ANI
* Support for EXE (comments, author, etc) (thanks p*quaedackers@planet*nl) * Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl)
* Support for DVD-IFO (region, subtitles, aspect ratio, etc) * Support for DVD-IFO (region, subtitles, aspect ratio, etc)
(thanks p*quaedackers@planet*nl) (thanks p*quaedackersØplanet*nl)
* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content * More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
(thanks n8n8@yahoo*com) (thanks n8n8Øyahoo*com)
* Support for a2b * Support for a2b
* Optional scan-through-frames for AVI verification * Optional scan-through-frames for AVI verification
(thanks rockcohen@massive-interactive*nl) (thanks rockcohenØmassive-interactive*nl)
* Support for TTF (thanks info@butterflyx*com) * Support for TTF (thanks infoØbutterflyx*com)
* Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171) * Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171)
* Support for SMAF (http://smaf-yamaha.com/what/demo.html) * Support for SMAF (http://smaf-yamaha.com/what/demo.html)
http://www.getid3.org/phpBB3/viewtopic.php?t=182 http://www.getid3.org/phpBB3/viewtopic.php?t=182
* Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195) * Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195) * Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2K@hotmail*com) * Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
* Parse XML data returned in Ogg comments * Parse XML data returned in Ogg comments
* Parse XML data from Quicktime SMIL metafiles (klausrath@mac*com) * Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
* ID3v2 genre string creator function * ID3v2 genre string creator function
* More complete parsing of JPG * More complete parsing of JPG
* Support for all old-style ASF packets * Support for all old-style ASF packets
@ -424,7 +425,7 @@ http://www.getid3.org/phpBB3/viewtopic.php?t=25
"movi" chunk that fits in the first 2GB, should issue error "movi" chunk that fits in the first 2GB, should issue error
to show that playtime is incorrect. Other data should be mostly to show that playtime is incorrect. Other data should be mostly
correct, assuming that data is constant throughout the file) correct, assuming that data is constant throughout the file)
* PHP <= v5 on Windows cannot read UTF-8 filenames
Known Bugs/Issues in other programs Known Bugs/Issues in other programs
@ -600,4 +601,4 @@ Reference material:
* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm * http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
* http://trac.musepack.net/trac/wiki/SV8Specification * http://trac.musepack.net/trac/wiki/SV8Specification
* http://wyday.com/cuesharp/specification.php * http://wyday.com/cuesharp/specification.php
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html