Update getID3 library to fix issues with PHP7.4

Updates to trunk version that includes fixes for PHP7.4

Changelog:
https://github.com/JamesHeinrich/getID3/compare/v1.9.14...00f3fbfd77e583099ca70a3cf0bc092e113d2b20

See: #47751,#47783.
Fixes: #48040.


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


git-svn-id: http://core.svn.wordpress.org/trunk@45924 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Aaron Jorbin 2019-09-14 19:07:57 +00:00
parent b7fbf7d373
commit 1ed691f574
19 changed files with 4285 additions and 2213 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,9 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// //
// Please see readme.txt for more information //
// ///
@ -26,6 +25,14 @@ if (!defined('ENT_SUBSTITUTE')) { // PHP5.3 adds ENT_IGNORE, PHP5.4 adds ENT_SUB
define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8));
}
/*
https://www.getid3.org/phpBB3/viewtopic.php?t=2114
If you are running into a the problem where filenames with special characters are being handled
incorrectly by external helper programs (e.g. metaflac), notably with the special characters removed,
and you are passing in the filename in UTF8 (typically via a HTML form), try uncommenting this line:
*/
//setlocale(LC_CTYPE, 'en_US.UTF-8');
// attempt to define temp dir as something flexible but reliable
$temp_dir = ini_get('upload_tmp_dir');
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
@ -74,53 +81,190 @@ unset($open_basedir, $temp_dir);
class getID3
{
// public: Settings
public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
/*
* Settings
*/
// public: Optional tag checks - disable for speed.
public $option_tag_id3v1 = true; // Read and process ID3v1 tags
public $option_tag_id3v2 = true; // Read and process ID3v2 tags
public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
public $option_tag_apetag = true; // Read and process APE tags
public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
/**
* CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
*
* @var string
*/
public $encoding = 'UTF-8';
// public: Optional tag/comment calucations
public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
/**
* Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
*
* @var string
*/
public $encoding_id3v1 = 'ISO-8859-1';
// public: Optional handling of embedded attachments (e.g. images)
public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
/*
* Optional tag checks - disable for speed.
*/
// public: Optional calculations
public $option_md5_data = false; // Get MD5 sum of data part - slow
public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
public $option_sha1_data = false; // Get SHA1 sum of data part - slow
public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
/**
* Read and process ID3v1 tags
*
* @var bool
*/
public $option_tag_id3v1 = true;
// public: Read buffer size in bytes
/**
* Read and process ID3v2 tags
*
* @var bool
*/
public $option_tag_id3v2 = true;
/**
* Read and process Lyrics3 tags
*
* @var bool
*/
public $option_tag_lyrics3 = true;
/**
* Read and process APE tags
*
* @var bool
*/
public $option_tag_apetag = true;
/**
* Copy tags to root key 'tags' and encode to $this->encoding
*
* @var bool
*/
public $option_tags_process = true;
/**
* Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
*
* @var bool
*/
public $option_tags_html = true;
/*
* Optional tag/comment calculations
*/
/**
* Calculate additional info such as bitrate, channelmode etc
*
* @var bool
*/
public $option_extra_info = true;
/*
* Optional handling of embedded attachments (e.g. images)
*/
/**
* Defaults to true (ATTACHMENTS_INLINE) for backward compatibility
*
* @var bool|string
*/
public $option_save_attachments = true;
/*
* Optional calculations
*/
/**
* Get MD5 sum of data part - slow
*
* @var bool
*/
public $option_md5_data = false;
/**
* Use MD5 of source file if availble - only FLAC and OptimFROG
*
* @var bool
*/
public $option_md5_data_source = false;
/**
* Get SHA1 sum of data part - slow
*
* @var bool
*/
public $option_sha1_data = false;
/**
* Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on
* PHP_INT_MAX)
*
* @var bool|null
*/
public $option_max_2gb_check;
/**
* Read buffer size in bytes
*
* @var int
*/
public $option_fread_buffer_size = 32768;
// Public variables
public $filename; // Filename of file being analysed.
public $fp; // Filepointer to file being analysed.
public $info; // Result array.
/**
* Filename of file being analysed.
*
* @var string
*/
public $filename;
/**
* Filepointer to file being analysed.
*
* @var resource
*/
public $fp;
/**
* Result array.
*
* @var array
*/
public $info;
/**
* @var string
*/
public $tempdir = GETID3_TEMP_DIR;
/**
* @var int
*/
public $memory_limit = 0;
// Protected variables
/**
* @var string
*/
protected $startup_error = '';
/**
* @var string
*/
protected $startup_warning = '';
const VERSION = '1.9.14-201706111222';
const VERSION = '1.9.17-201907240906';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
const ATTACHMENTS_INLINE = true;
// public: constructor
public function __construct() {
// Check for PHP version
$required_php_version = '5.3.0';
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."\n";
return;
}
// Check memory
$this->memory_limit = ini_get('memory_limit');
if (preg_match('#([0-9]+) ?M#i', $this->memory_limit, $matches)) {
@ -176,7 +320,7 @@ class getID3
// Needed for Windows only:
// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
// as well as other helper functions such as head, tail, md5sum, etc
// as well as other helper functions such as head, etc
// This path cannot contain spaces, but the below code will attempt to get the
// 8.3-equivalent path automatically
// IMPORTANT: This path must include the trailing slash
@ -219,20 +363,27 @@ class getID3
echo $this->startup_error;
throw new getid3_exception($this->startup_error);
}
return true;
}
/**
* @return string
*/
public function version() {
return self::VERSION;
}
/**
* @return int
*/
public function fread_buffer_size() {
return $this->option_fread_buffer_size;
}
// public: setOption
/**
* @param array $optArray
*
* @return bool
*/
public function setOption($optArray) {
if (!is_array($optArray) || empty($optArray)) {
return false;
@ -246,8 +397,15 @@ class getID3
return true;
}
public function openfile($filename, $filesize=null) {
/**
* @param string $filename
* @param int $filesize
*
* @return bool
*
* @throws getid3_exception
*/
public function openfile($filename, $filesize=null, $fp=null) {
try {
if (!empty($this->startup_error)) {
throw new getid3_exception($this->startup_error);
@ -270,11 +428,13 @@ class getID3
}
$filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
$filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
//$filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
// open local file
//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'))) {
//if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720
if (($fp != null) && ((get_resource_type($fp) == 'file') || (get_resource_type($fp) == 'stream'))) {
$this->fp = $fp;
} elseif ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
// great
} else {
$errormessagelist = array();
@ -331,10 +491,10 @@ class getID3
} elseif (getid3_lib::intValueSupported($real_filesize)) {
unset($this->info['filesize']);
fclose($this->fp);
throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize / 1073741824, 3).'GB, please report to info@getid3.org');
}
$this->info['filesize'] = $real_filesize;
$this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
$this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize / 1073741824, 3).'GB) and is not properly supported by PHP.');
}
}
@ -346,10 +506,18 @@ class getID3
return false;
}
// public: analyze file
public function analyze($filename, $filesize=null, $original_filename='') {
/**
* analyze file
*
* @param string $filename
* @param int $filesize
* @param string $original_filename
*
* @return array
*/
public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
try {
if (!$this->openfile($filename, $filesize)) {
if (!$this->openfile($filename, $filesize, $fp)) {
return $this->info;
}
@ -383,8 +551,8 @@ class getID3
$header = fread($this->fp, 10);
if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
$this->info['id3v2']['header'] = true;
$this->info['id3v2']['majorversion'] = ord($header{3});
$this->info['id3v2']['minorversion'] = ord($header{4});
$this->info['id3v2']['majorversion'] = ord($header[3]);
$this->info['id3v2']['minorversion'] = ord($header[4]);
$this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
}
}
@ -497,7 +665,13 @@ class getID3
}
// private: error handling
/**
* Error handling.
*
* @param string $message
*
* @return array
*/
public function error($message) {
$this->CleanUp();
if (!isset($this->info['error'])) {
@ -508,14 +682,22 @@ class getID3
}
// private: warning handling
/**
* Warning handling.
*
* @param string $message
*
* @return bool
*/
public function warning($message) {
$this->info['warning'][] = $message;
return true;
}
// private: CleanUp
/**
* @return bool
*/
private function CleanUp() {
// remove possible empty keys
@ -562,8 +744,11 @@ class getID3
return true;
}
// return array containing information about all supported formats
/**
* Return array containing information about all supported formats.
*
* @return array
*/
public function GetFileFormatArray() {
static $format_info = array();
if (empty($format_info)) {
@ -584,7 +769,7 @@ class getID3
'pattern' => '^ADIF',
'group' => 'audio',
'module' => 'aac',
'mime_type' => 'application/octet-stream',
'mime_type' => 'audio/aac',
'fail_ape' => 'WARNING',
),
@ -602,7 +787,7 @@ class getID3
'pattern' => '^\\xFF[\\xF0-\\xF1\\xF8-\\xF9]',
'group' => 'audio',
'module' => 'aac',
'mime_type' => 'application/octet-stream',
'mime_type' => 'audio/aac',
'fail_ape' => 'WARNING',
),
@ -649,7 +834,7 @@ class getID3
// DSS - audio - Digital Speech Standard
'dss' => array(
'pattern' => '^[\\x02-\\x06]ds[s2]',
'pattern' => '^[\\x02-\\x08]ds[s2]',
'group' => 'audio',
'module' => 'dss',
'mime_type' => 'application/octet-stream',
@ -668,7 +853,7 @@ class getID3
'pattern' => '^fLaC',
'group' => 'audio',
'module' => 'flac',
'mime_type' => 'audio/x-flac',
'mime_type' => 'audio/flac',
),
// LA - audio - Lossless Audio (LA)
@ -700,7 +885,7 @@ class getID3
'pattern' => '^MAC ',
'group' => 'audio',
'module' => 'monkey',
'mime_type' => 'application/octet-stream',
'mime_type' => 'audio/x-monkeys-audio',
),
// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
@ -889,7 +1074,7 @@ class getID3
'pattern' => '^(RIFF|SDSS|FORM)',
'group' => 'audio-video',
'module' => 'riff',
'mime_type' => 'audio/x-wav',
'mime_type' => 'audio/wav',
'fail_ape' => 'WARNING',
),
@ -1053,7 +1238,7 @@ class getID3
'pattern' => '^\\x1F\\x8B\\x08',
'group' => 'archive',
'module' => 'gzip',
'mime_type' => 'application/x-gzip',
'mime_type' => 'application/gzip',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
),
@ -1068,6 +1253,16 @@ class getID3
'fail_ape' => 'ERROR',
),
// XZ - data - XZ compressed data
'xz' => array(
'pattern' => '^\\xFD7zXZ\\x00',
'group' => 'archive',
'module' => 'xz',
'mime_type' => 'application/x-xz',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
),
// Misc other formats
@ -1115,8 +1310,12 @@ class getID3
return $format_info;
}
/**
* @param string $filedata
* @param string $filename
*
* @return mixed|false
*/
public function GetFileFormat(&$filedata, $filename='') {
// this function will determine the format of a file based on usually
// the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
@ -1135,7 +1334,7 @@ class getID3
if (preg_match('#\\.mp[123a]$#i', $filename)) {
// Too many mp3 encoders on the market put gabage in front of mpeg files
// Too many mp3 encoders on the market put garbage in front of mpeg files
// use assume format on these if format detection failed
$GetFileFormatArray = $this->GetFileFormatArray();
$info = $GetFileFormatArray['mp3'];
@ -1154,8 +1353,12 @@ class getID3
return false;
}
// converts array to $encoding charset from $this->encoding
/**
* Converts array to $encoding charset from $this->encoding.
*
* @param array $array
* @param string $encoding
*/
public function CharConvert(&$array, $encoding) {
// identical encoding - end here
@ -1178,7 +1381,9 @@ class getID3
}
}
/**
* @return bool
*/
public function HandleAllTags() {
// key name => array (tag name, character encoding)
@ -1233,6 +1438,7 @@ class getID3
}
}
if ($tag_key == 'picture') {
// pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
unset($this->info[$comment_name]['comments'][$tag_key]);
}
}
@ -1246,6 +1452,11 @@ class getID3
if ($this->option_tags_html) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
if ($tag_key == 'picture') {
// Do not to try to convert binary picture data to HTML
// https://github.com/JamesHeinrich/getID3/issues/178
continue;
}
$this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
}
}
@ -1254,8 +1465,7 @@ class getID3
}
// pictures can take up a lot of space, and we don't need multiple copies of them
// 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; let there be a single copy in [comments][picture], and not elsewhere
if (!empty($this->info['tags'])) {
$unset_keys = array('tags', 'tags_html');
foreach ($this->info['tags'] as $tagtype => $tagarray) {
@ -1301,6 +1511,11 @@ class getID3
return true;
}
/**
* @param string $algorithm
*
* @return array|bool
*/
public function getHashdata($algorithm) {
switch ($algorithm) {
case 'md5':
@ -1365,7 +1580,6 @@ class getID3
} else {
$commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
$commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
$VorbisCommentError = `$commandline`;
@ -1424,7 +1638,6 @@ class getID3
return true;
}
public function ChannelsBitratePlaytimeCalculations() {
// set channelmode on audio
@ -1489,7 +1702,9 @@ class getID3
}
}
/**
* @return bool
*/
public function CalculateCompressionRatioVideo() {
if (empty($this->info['video'])) {
return false;
@ -1537,7 +1752,9 @@ class getID3
return true;
}
/**
* @return bool
*/
public function CalculateCompressionRatioAudio() {
if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
return false;
@ -1554,11 +1771,13 @@ class getID3
return true;
}
/**
* @return bool
*/
public function CalculateReplayGain() {
if (isset($this->info['replay_gain'])) {
if (!isset($this->info['replay_gain']['reference_volume'])) {
$this->info['replay_gain']['reference_volume'] = (double) 89.0;
$this->info['replay_gain']['reference_volume'] = 89.0;
}
if (isset($this->info['replay_gain']['track']['adjustment'])) {
$this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
@ -1577,6 +1796,9 @@ class getID3
return true;
}
/**
* @return bool
*/
public function ProcessAudioStreams() {
if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
if (!isset($this->info['audio']['streams'])) {
@ -1590,10 +1812,20 @@ class getID3
return true;
}
/**
* @return string|bool
*/
public function getid3_tempnam() {
return tempnam($this->tempdir, 'gI3');
}
/**
* @param string $name
*
* @return bool
*
* @throws getid3_exception
*/
public function include_module($name) {
//if (!file_exists($this->include_path.'module.'.$name.'.php')) {
if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
@ -1603,35 +1835,70 @@ class getID3
return true;
}
public static function is_writable ($filename) {
$ret = is_writable($filename);
if (!$ret) {
$perms = fileperms($filename);
$ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
}
return $ret;
}
/**
* @param string $filename
*
* @return bool
*/
public static function is_writable ($filename) {
$ret = is_writable($filename);
if (!$ret) {
$perms = fileperms($filename);
$ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
}
return $ret;
}
}
abstract class getid3_handler {
abstract class getid3_handler
{
/**
* @var getID3
*/
protected $getid3; // pointer
protected $data_string_flag = false; // analyzing filepointer or string
protected $data_string = ''; // string to analyze
protected $data_string_position = 0; // seek position in string
protected $data_string_length = 0; // string length
/**
* Analyzing filepointer or string.
*
* @var bool
*/
protected $data_string_flag = false;
private $dependency_to = null;
/**
* String to analyze.
*
* @var string
*/
protected $data_string = '';
/**
* Seek position in string.
*
* @var int
*/
protected $data_string_position = 0;
/**
* String length.
*
* @var int
*/
protected $data_string_length = 0;
/**
* @var string
*/
private $dependency_to;
/**
* getid3_handler constructor.
*
* @param getID3 $getid3
* @param string $call_module
*/
public function __construct(getID3 $getid3, $call_module=null) {
$this->getid3 = $getid3;
@ -1640,12 +1907,18 @@ abstract class getid3_handler {
}
}
// Analyze from file pointer
/**
* Analyze from file pointer.
*
* @return bool
*/
abstract public function Analyze();
// Analyze from string instead
/**
* Analyze from string instead.
*
* @param string $string
*/
public function AnalyzeString($string) {
// Enter string mode
$this->setStringMode($string);
@ -1671,12 +1944,18 @@ abstract class getid3_handler {
$this->data_string_flag = false;
}
/**
* @param string $string
*/
public function setStringMode($string) {
$this->data_string_flag = true;
$this->data_string = $string;
$this->data_string_length = strlen($string);
}
/**
* @return int|bool
*/
protected function ftell() {
if ($this->data_string_flag) {
return $this->data_string_position;
@ -1684,6 +1963,13 @@ abstract class getid3_handler {
return ftell($this->getid3->fp);
}
/**
* @param int $bytes
*
* @return string|false
*
* @throws getid3_exception
*/
protected function fread($bytes) {
if ($this->data_string_flag) {
$this->data_string_position += $bytes;
@ -1696,7 +1982,7 @@ abstract class getid3_handler {
//return fread($this->getid3->fp, $bytes);
/*
* http://www.getid3.org/phpBB3/viewtopic.php?t=1930
* https://www.getid3.org/phpBB3/viewtopic.php?t=1930
* "I found out that the root cause for the problem was how getID3 uses the PHP system function fread().
* It seems to assume that fread() would always return as many bytes as were requested.
* However, according the PHP manual (http://php.net/manual/en/function.fread.php), this is the case only with regular local files, but not e.g. with Linux pipes.
@ -1704,6 +1990,10 @@ abstract class getid3_handler {
*/
$contents = '';
do {
//if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
if (($this->getid3->memory_limit > 0) && (($bytes / $this->getid3->memory_limit) > 0.99)) { // enable a more-fuzzy match to prevent close misses generating errors like "PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554464 bytes)"
throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
}
$part = fread($this->getid3->fp, $bytes);
$partLength = strlen($part);
$bytes -= $partLength;
@ -1712,6 +2002,14 @@ abstract class getid3_handler {
return $contents;
}
/**
* @param int $bytes
* @param int $whence
*
* @return int
*
* @throws getid3_exception
*/
protected function fseek($bytes, $whence=SEEK_SET) {
if ($this->data_string_flag) {
switch ($whence) {
@ -1742,6 +2040,9 @@ abstract class getid3_handler {
return fseek($this->getid3->fp, $bytes, $whence);
}
/**
* @return bool
*/
protected function feof() {
if ($this->data_string_flag) {
return $this->data_string_position >= $this->data_string_length;
@ -1749,24 +2050,53 @@ abstract class getid3_handler {
return feof($this->getid3->fp);
}
/**
* @param string $module
*
* @return bool
*/
final protected function isDependencyFor($module) {
return $this->dependency_to == $module;
}
/**
* @param string $text
*
* @return bool
*/
protected function error($text) {
$this->getid3->info['error'][] = $text;
return false;
}
/**
* @param string $text
*
* @return bool
*/
protected function warning($text) {
return $this->getid3->warning($text);
}
/**
* @param string $text
*/
protected function notice($text) {
// does nothing for now
}
/**
* @param string $name
* @param int $offset
* @param int $length
* @param string $image_mime
*
* @return string|null
*
* @throws Exception
* @throws getid3_exception
*/
public function saveAttachment($name, $offset, $length, $image_mime=null) {
try {
@ -1820,6 +2150,9 @@ abstract class getid3_handler {
// close and remove dest file if created
if (isset($fp_dest) && is_resource($fp_dest)) {
fclose($fp_dest);
}
if (isset($dest) && file_exists($dest)) {
unlink($dest);
}

View File

@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// or https://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
@ -18,9 +18,9 @@ GNU GPL: https://gnu.org/licenses/gpl.html (v3)
GNU LGPL: https://gnu.org/licenses/lgpl.html (v3)
Mozilla MPL: http://www.mozilla.org/MPL/2.0/ (v2)
Mozilla MPL: https://www.mozilla.org/MPL/2.0/ (v2)
getID3 Commercial License: http://getid3.org/#gCL (payment required)
getID3 Commercial License: https://www.getid3.org/#gCL (payment required)
*****************************************************************
*****************************************************************

View File

@ -1,11 +1,10 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.asf.php //
@ -16,8 +15,11 @@
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_asf extends getid3_handler {
class getid3_asf extends getid3_handler
{
/**
* @param getID3 $getid3
*/
public function __construct(getID3 $getid3) {
parent::__construct($getid3); // extends getid3_handler::__construct()
@ -30,6 +32,9 @@ class getid3_asf extends getid3_handler {
}
}
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -83,6 +88,8 @@ class getid3_asf extends getid3_handler {
$NextObjectOffset = $this->ftell();
$ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30);
$offset = 0;
$thisfile_asf_streambitratepropertiesobject = array();
$thisfile_asf_codeclistobject = array();
for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
$NextObjectGUID = substr($ASFHeaderData, $offset, 16);
@ -790,17 +797,17 @@ class getid3_asf extends getid3_handler {
case 'wm/tracknumber':
case 'tracknumber':
// be careful casting to int: casting unicode strings to int gives unexpected results (stops parsing at first non-numeric character)
$thisfile_asf_comments['track'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
foreach ($thisfile_asf_comments['track'] as $key => $value) {
$thisfile_asf_comments['track_number'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
foreach ($thisfile_asf_comments['track_number'] as $key => $value) {
if (preg_match('/^[0-9\x00]+$/', $value)) {
$thisfile_asf_comments['track'][$key] = intval(str_replace("\x00", '', $value));
$thisfile_asf_comments['track_number'][$key] = intval(str_replace("\x00", '', $value));
}
}
break;
case 'wm/track':
if (empty($thisfile_asf_comments['track'])) {
$thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
if (empty($thisfile_asf_comments['track_number'])) {
$thisfile_asf_comments['track_number'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
}
break;
@ -970,18 +977,18 @@ class getid3_asf extends getid3_handler {
break;
}
}
if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) {
if (isset($thisfile_asf_streambitratepropertiesobject['bitrate_records_count'])) {
$ASFbitrateAudio = 0;
$ASFbitrateVideo = 0;
for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) {
for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) {
if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) {
switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) {
case 1:
$ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
$ASFbitrateVideo += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
break;
case 2:
$ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
$ASFbitrateAudio += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
break;
default:
@ -1440,6 +1447,11 @@ class getid3_asf extends getid3_handler {
return true;
}
/**
* @param int $CodecListType
*
* @return string
*/
public static function codecListObjectTypeLookup($CodecListType) {
static $lookup = array(
0x0001 => 'Video Codec',
@ -1450,6 +1462,9 @@ class getid3_asf extends getid3_handler {
return (isset($lookup[$CodecListType]) ? $lookup[$CodecListType] : 'Invalid Codec Type');
}
/**
* @return array
*/
public static function KnownGUIDs() {
static $GUIDarray = array(
'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A',
@ -1564,6 +1579,11 @@ class getid3_asf extends getid3_handler {
return $GUIDarray;
}
/**
* @param string $GUIDstring
*
* @return string|false
*/
public static function GUIDname($GUIDstring) {
static $GUIDarray = array();
if (empty($GUIDarray)) {
@ -1572,6 +1592,11 @@ class getid3_asf extends getid3_handler {
return array_search($GUIDstring, $GUIDarray);
}
/**
* @param int $id
*
* @return string
*/
public static function ASFIndexObjectIndexTypeLookup($id) {
static $ASFIndexObjectIndexTypeLookup = array();
if (empty($ASFIndexObjectIndexTypeLookup)) {
@ -1582,6 +1607,11 @@ class getid3_asf extends getid3_handler {
return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid');
}
/**
* @param string $GUIDstring
*
* @return string
*/
public static function GUIDtoBytestring($GUIDstring) {
// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
// first 4 bytes are in little-endian order
@ -1617,31 +1647,42 @@ class getid3_asf extends getid3_handler {
return $hexbytecharstring;
}
/**
* @param string $Bytestring
*
* @return string
*/
public static function BytestringToGUID($Bytestring) {
$GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{0})), 2, '0', STR_PAD_LEFT);
$GUIDstring = str_pad(dechex(ord($Bytestring[3])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[2])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[1])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[0])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
$GUIDstring .= str_pad(dechex(ord($Bytestring{5})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{4})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[5])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[4])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
$GUIDstring .= str_pad(dechex(ord($Bytestring{7})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{6})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[7])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[6])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
$GUIDstring .= str_pad(dechex(ord($Bytestring{8})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{9})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[8])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[9])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
$GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[10])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[11])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[12])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[13])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[14])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring[15])), 2, '0', STR_PAD_LEFT);
return strtoupper($GUIDstring);
}
/**
* @param int $FILETIME
* @param bool $round
*
* @return float|int
*/
public static function FILETIMEtoUNIXtime($FILETIME, $round=true) {
// FILETIME is a 64-bit unsigned integer representing
// the number of 100-nanosecond intervals since January 1, 1601
@ -1653,6 +1694,11 @@ class getid3_asf extends getid3_handler {
return ($FILETIME - 116444736000000000) / 10000000;
}
/**
* @param int $WMpictureType
*
* @return string
*/
public static function WMpictureTypeLookup($WMpictureType) {
static $lookup = null;
if ($lookup === null) {
@ -1684,6 +1730,12 @@ class getid3_asf extends getid3_handler {
return (isset($lookup[$WMpictureType]) ? $lookup[$WMpictureType] : '');
}
/**
* @param string $asf_header_extension_object_data
* @param int $unhandled_sections
*
* @return array
*/
public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) {
// http://msdn.microsoft.com/en-us/library/bb643323.aspx
@ -1930,7 +1982,11 @@ class getid3_asf extends getid3_handler {
return $HeaderExtensionObjectParsed;
}
/**
* @param int $id
*
* @return string
*/
public static function metadataLibraryObjectDataTypeLookup($id) {
static $lookup = array(
0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters
@ -1944,6 +2000,11 @@ class getid3_asf extends getid3_handler {
return (isset($lookup[$id]) ? $lookup[$id] : 'invalid');
}
/**
* @param string $data
*
* @return array
*/
public function ASF_WMpicture(&$data) {
//typedef struct _WMPicture{
// LPWSTR pwszMIMEType;
@ -1994,14 +2055,24 @@ class getid3_asf extends getid3_handler {
return $WMpicture;
}
// Remove terminator 00 00 and convert UTF-16LE to Latin-1
/**
* Remove terminator 00 00 and convert UTF-16LE to Latin-1.
*
* @param string $string
*
* @return string
*/
public static function TrimConvert($string) {
return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', self::TrimTerm($string)), ' ');
}
// Remove terminator 00 00
/**
* Remove terminator 00 00.
*
* @param string $string
*
* @return string
*/
public static function TrimTerm($string) {
// remove terminator, only if present (it should be, but...)
if (substr($string, -2) === "\x00\x00") {

View File

@ -1,15 +1,22 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.flv.php //
// module for analyzing Shockwave Flash Video files //
// dependencies: NONE //
// //
/////////////////////////////////////////////////////////////////
// //
// FLV module by Seth Kaufman <sethØwhirl-i-gig*com> //
// //
// * version 0.1 (26 June 2005) //
// //
// //
// * version 0.1.1 (15 July 2005) //
// minor modifications by James Heinrich <info@getid3.org> //
// //
@ -43,12 +50,6 @@
// handle GETID3_FLV_VIDEO_VP6FLV_ALPHA //
// improved AVCSequenceParameterSetReader::readData() //
// by Xander Schouwerwou <schouwerwouØgmail*com> //
// //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.flv.php //
// module for analyzing Shockwave Flash Video files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
@ -73,12 +74,21 @@ define('H264_PROFILE_HIGH422', 122);
define('H264_PROFILE_HIGH444', 144);
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
/**
* Break out of the loop if too many frames have been scanned; only scan this
* many if meta frame does not contain useful duration.
*
* @var int
*/
public $max_frames = 100000;
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -332,7 +342,11 @@ class getid3_flv extends getid3_handler {
return true;
}
/**
* @param int $id
*
* @return string|false
*/
public static function audioFormatLookup($id) {
static $lookup = array(
0 => 'Linear PCM, platform endian',
@ -355,6 +369,11 @@ class getid3_flv extends getid3_handler {
return (isset($lookup[$id]) ? $lookup[$id] : false);
}
/**
* @param int $id
*
* @return int|false
*/
public static function audioRateLookup($id) {
static $lookup = array(
0 => 5500,
@ -365,6 +384,11 @@ class getid3_flv extends getid3_handler {
return (isset($lookup[$id]) ? $lookup[$id] : false);
}
/**
* @param int $id
*
* @return int|false
*/
public static function audioBitDepthLookup($id) {
static $lookup = array(
0 => 8,
@ -373,6 +397,11 @@ class getid3_flv extends getid3_handler {
return (isset($lookup[$id]) ? $lookup[$id] : false);
}
/**
* @param int $id
*
* @return string|false
*/
public static function videoCodecLookup($id) {
static $lookup = array(
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
@ -386,47 +415,84 @@ class getid3_flv extends getid3_handler {
}
}
class AMFStream {
class AMFStream
{
/**
* @var string
*/
public $bytes;
/**
* @var int
*/
public $pos;
/**
* @param string $bytes
*/
public function __construct(&$bytes) {
$this->bytes =& $bytes;
$this->pos = 0;
}
public function readByte() {
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
/**
* @return int
*/
public function readByte() { // 8-bit
return ord(substr($this->bytes, $this->pos++, 1));
}
public function readInt() {
/**
* @return int
*/
public function readInt() { // 16-bit
return ($this->readByte() << 8) + $this->readByte();
}
public function readLong() {
/**
* @return int
*/
public function readLong() { // 32-bit
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
}
/**
* @return float|false
*/
public function readDouble() {
return getid3_lib::BigEndian2Float($this->read(8));
}
/**
* @return string
*/
public function readUTF() {
$length = $this->readInt();
return $this->read($length);
}
/**
* @return string
*/
public function readLongUTF() {
$length = $this->readLong();
return $this->read($length);
}
/**
* @param int $length
*
* @return string
*/
public function read($length) {
$val = substr($this->bytes, $this->pos, $length);
$this->pos += $length;
return $val;
}
/**
* @return int
*/
public function peekByte() {
$pos = $this->pos;
$val = $this->readByte();
@ -434,6 +500,9 @@ class AMFStream {
return $val;
}
/**
* @return int
*/
public function peekInt() {
$pos = $this->pos;
$val = $this->readInt();
@ -441,6 +510,9 @@ class AMFStream {
return $val;
}
/**
* @return int
*/
public function peekLong() {
$pos = $this->pos;
$val = $this->readLong();
@ -448,6 +520,9 @@ class AMFStream {
return $val;
}
/**
* @return float|false
*/
public function peekDouble() {
$pos = $this->pos;
$val = $this->readDouble();
@ -455,6 +530,9 @@ class AMFStream {
return $val;
}
/**
* @return string
*/
public function peekUTF() {
$pos = $this->pos;
$val = $this->readUTF();
@ -462,6 +540,9 @@ class AMFStream {
return $val;
}
/**
* @return string
*/
public function peekLongUTF() {
$pos = $this->pos;
$val = $this->readLongUTF();
@ -470,13 +551,23 @@ class AMFStream {
}
}
class AMFReader {
class AMFReader
{
/**
* @var AMFStream
*/
public $stream;
public function __construct(&$stream) {
$this->stream =& $stream;
/**
* @param AMFStream $stream
*/
public function __construct(AMFStream $stream) {
$this->stream = $stream;
}
/**
* @return mixed
*/
public function readData() {
$value = null;
@ -547,23 +638,36 @@ class AMFReader {
return $value;
}
/**
* @return float|false
*/
public function readDouble() {
return $this->stream->readDouble();
}
/**
* @return bool
*/
public function readBoolean() {
return $this->stream->readByte() == 1;
}
/**
* @return string
*/
public function readString() {
return $this->stream->readUTF();
}
/**
* @return array
*/
public function readObject() {
// Get highest numerical index - ignored
// $highestIndex = $this->stream->readLong();
$data = array();
$key = null;
while ($key = $this->stream->readUTF()) {
$data[$key] = $this->readData();
@ -576,15 +680,19 @@ class AMFReader {
return $data;
}
/**
* @return array
*/
public function readMixedArray() {
// Get highest numerical index - ignored
$highestIndex = $this->stream->readLong();
$data = array();
$key = null;
while ($key = $this->stream->readUTF()) {
if (is_numeric($key)) {
$key = (float) $key;
$key = (int) $key;
}
$data[$key] = $this->readData();
}
@ -597,6 +705,9 @@ class AMFReader {
return $data;
}
/**
* @return array
*/
public function readArray() {
$length = $this->stream->readLong();
$data = array();
@ -607,34 +718,61 @@ class AMFReader {
return $data;
}
/**
* @return float|false
*/
public function readDate() {
$timestamp = $this->stream->readDouble();
$timezone = $this->stream->readInt();
return $timestamp;
}
/**
* @return string
*/
public function readLongString() {
return $this->stream->readLongUTF();
}
/**
* @return string
*/
public function readXML() {
return $this->stream->readLongUTF();
}
/**
* @return array
*/
public function readTypedObject() {
$className = $this->stream->readUTF();
return $this->readObject();
}
}
class AVCSequenceParameterSetReader {
class AVCSequenceParameterSetReader
{
/**
* @var string
*/
public $sps;
public $start = 0;
public $currentBytes = 0;
public $currentBits = 0;
/**
* @var int
*/
public $width;
/**
* @var int
*/
public $height;
/**
* @param string $sps
*/
public function __construct($sps) {
$this->sps = $sps;
}
@ -691,18 +829,29 @@ class AVCSequenceParameterSetReader {
}
}
/**
* @param int $bits
*/
public function skipBits($bits) {
$newBits = $this->currentBits + $bits;
$this->currentBytes += (int)floor($newBits / 8);
$this->currentBits = $newBits % 8;
}
/**
* @return int
*/
public function getBit() {
$result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
$this->skipBits(1);
return $result;
}
/**
* @param int $bits
*
* @return int
*/
public function getBits($bits) {
$result = 0;
for ($i = 0; $i < $bits; $i++) {
@ -711,6 +860,9 @@ class AVCSequenceParameterSetReader {
return $result;
}
/**
* @return int
*/
public function expGolombUe() {
$significantBits = 0;
$bit = $this->getBit();
@ -726,6 +878,9 @@ class AVCSequenceParameterSetReader {
return (1 << $significantBits) + $this->getBits($significantBits) - 1;
}
/**
* @return int
*/
public function expGolombSe() {
$result = $this->expGolombUe();
if (($result & 0x01) == 0) {
@ -735,10 +890,16 @@ class AVCSequenceParameterSetReader {
}
}
/**
* @return int
*/
public function getWidth() {
return $this->width;
}
/**
* @return int
*/
public function getHeight() {
return $this->height;
}

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.matriska.php //
@ -72,7 +72,7 @@ define('EBML_ID_FILEREFERRAL', 0x0675); // [46][75] --
define('EBML_ID_FILEDESCRIPTION', 0x067E); // [46][7E] -- A human-friendly name for the attached file.
define('EBML_ID_FILEUID', 0x06AE); // [46][AE] -- Unique ID representing the file, as random as possible.
define('EBML_ID_CONTENTENCALGO', 0x07E1); // [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the data was encrypted with.
define('EBML_ID_CONTENTSIGNATURE', 0x07E3); // [47][E3] -- A cryptographic signature of the contents.
define('EBML_ID_CONTENTSIGKEYID', 0x07E4); // [47][E4] -- This is the ID of the private key the data was signed with.
define('EBML_ID_CONTENTSIGALGO', 0x07E5); // [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
@ -215,17 +215,33 @@ define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] --
*/
class getid3_matroska extends getid3_handler
{
// public options
public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE]
public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
/**
* If true, do not return information about CLUSTER chunks, since there's a lot of them
* and they're not usually useful [default: TRUE].
*
* @var bool
*/
public static $hide_clusters = true;
// private parser settings/placeholders
/**
* True to parse the whole file, not only header [default: FALSE].
*
* @var bool
*/
public static $parse_whole_file = false;
/*
* Private parser settings/placeholders.
*/
private $EBMLbuffer = '';
private $EBMLbuffer_offset = 0;
private $EBMLbuffer_length = 0;
private $current_offset = 0;
private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
/**
* @return bool
*/
public function Analyze()
{
$info = &$this->getid3->info;
@ -366,8 +382,8 @@ class getid3_matroska extends getid3_handler
if (!empty($getid3_temp->info[$header_data_key])) {
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
foreach ($getid3_temp->info['audio'] as $key => $value) {
$track_info[$key] = $value;
foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
$track_info[$sub_key] = $value;
}
}
}
@ -421,8 +437,8 @@ class getid3_matroska extends getid3_handler
if (!empty($getid3_temp->info['ogg'])) {
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
foreach ($getid3_temp->info['audio'] as $key => $value) {
$track_info[$key] = $value;
foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
$track_info[$sub_key] = $value;
}
}
}
@ -449,9 +465,9 @@ class getid3_matroska extends getid3_handler
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
foreach ($parsed as $key => $value) {
if ($key != 'raw') {
$track_info[$key] = $value;
foreach ($parsed as $sub_key => $value) {
if ($sub_key != 'raw') {
$track_info[$sub_key] = $value;
}
}
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
@ -496,6 +512,9 @@ class getid3_matroska extends getid3_handler
return true;
}
/**
* @param array $info
*/
private function parseEBML(&$info) {
// http://www.matroska.org/technical/specs/index.html#EBMLBasics
$this->current_offset = $info['avdataoffset'];
@ -1228,6 +1247,11 @@ class getid3_matroska extends getid3_handler
}
}
/**
* @param int $min_data
*
* @return bool
*/
private function EnsureBufferHasEnoughData($min_data=1024) {
if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
$read_bytes = max($min_data, $this->getid3->fread_buffer_size());
@ -1249,6 +1273,9 @@ class getid3_matroska extends getid3_handler
return true;
}
/**
* @return int|float|false
*/
private function readEBMLint() {
$actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
@ -1281,6 +1308,12 @@ class getid3_matroska extends getid3_handler
return $int_value;
}
/**
* @param int $length
* @param bool $check_buffer
*
* @return string|false
*/
private function readEBMLelementData($length, $check_buffer=false) {
if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
return false;
@ -1290,6 +1323,13 @@ class getid3_matroska extends getid3_handler
return $data;
}
/**
* @param array $element
* @param int $parent_end
* @param array|bool $get_data
*
* @return bool
*/
private function getEBMLelement(&$element, $parent_end, $get_data=false) {
if ($this->current_offset >= $parent_end) {
return false;
@ -1326,6 +1366,11 @@ class getid3_matroska extends getid3_handler
return true;
}
/**
* @param string $type
* @param int $line
* @param array $element
*/
private function unhandledElement($type, $line, $element) {
// warn only about unknown and missed elements, not about unuseful
if (!in_array($element['id'], $this->unuseful_elements)) {
@ -1338,6 +1383,11 @@ class getid3_matroska extends getid3_handler
}
}
/**
* @param array $SimpleTagArray
*
* @return bool
*/
private function ExtractCommentsSimpleTag($SimpleTagArray) {
if (!empty($SimpleTagArray['SimpleTag'])) {
foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
@ -1353,6 +1403,11 @@ class getid3_matroska extends getid3_handler
return true;
}
/**
* @param int $parent_end
*
* @return array
*/
private function HandleEMBLSimpleTag($parent_end) {
$simpletag_entry = array();
@ -1383,6 +1438,13 @@ class getid3_matroska extends getid3_handler
return $simpletag_entry;
}
/**
* @param array $element
* @param int $block_type
* @param array $info
*
* @return array
*/
private function HandleEMBLClusterBlock($element, $block_type, &$info) {
// http://www.matroska.org/technical/specs/index.html#block_structure
// http://www.matroska.org/technical/specs/index.html#simpleblock_structure
@ -1446,6 +1508,11 @@ class getid3_matroska extends getid3_handler
return $block_data;
}
/**
* @param string $EBMLstring
*
* @return int|float|false
*/
private static function EBML2Int($EBMLstring) {
// http://matroska.org/specs/
@ -1488,12 +1555,22 @@ class getid3_matroska extends getid3_handler
return getid3_lib::BigEndian2Int($EBMLstring);
}
/**
* @param int $EBMLdatestamp
*
* @return float
*/
private static function EBMLdate2unix($EBMLdatestamp) {
// Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
// 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
return round(($EBMLdatestamp / 1000000000) + 978307200);
}
/**
* @param int $target_type
*
* @return string|int
*/
public static function TargetTypeValue($target_type) {
// http://www.matroska.org/technical/specs/tagging/index.html
static $TargetTypeValue = array();
@ -1509,6 +1586,11 @@ class getid3_matroska extends getid3_handler
return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
}
/**
* @param int $lacingtype
*
* @return string|int
*/
public static function BlockLacingType($lacingtype) {
// http://matroska.org/technical/specs/index.html#block_structure
static $BlockLacingType = array();
@ -1521,6 +1603,11 @@ class getid3_matroska extends getid3_handler
return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
}
/**
* @param string $codecid
*
* @return string
*/
public static function CodecIDtoCommonName($codecid) {
// http://www.matroska.org/technical/specs/codecid/index.html
static $CodecIDlist = array();
@ -1557,6 +1644,11 @@ class getid3_matroska extends getid3_handler
return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
}
/**
* @param int $value
*
* @return string
*/
private static function EBMLidName($value) {
static $EBMLidList = array();
if (empty($EBMLidList)) {
@ -1755,6 +1847,11 @@ class getid3_matroska extends getid3_handler
return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
}
/**
* @param int $value
*
* @return string
*/
public static function displayUnit($value) {
// http://www.matroska.org/technical/specs/index.html#DisplayUnit
static $units = array(
@ -1766,8 +1863,14 @@ class getid3_matroska extends getid3_handler
return (isset($units[$value]) ? $units[$value] : 'unknown');
}
/**
* @param array $streams
*
* @return array
*/
private static function getDefaultStreamInfo($streams)
{
$stream = array();
foreach (array_reverse($streams) as $stream) {
if ($stream['default']) {
break;

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.riff.php //
@ -27,10 +27,15 @@ 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.dts.php', __FILE__, true);
class getid3_riff extends getid3_handler {
class getid3_riff extends getid3_handler
{
protected $container = 'riff'; // default
/**
* @return bool
*
* @throws getid3_exception
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -46,6 +51,7 @@ class getid3_riff extends getid3_handler {
$thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
$thisfile_riff_audio = &$thisfile_riff['audio'];
$thisfile_riff_video = &$thisfile_riff['video'];
$thisfile_riff_WAVE = array();
$Original['avdataoffset'] = $info['avdataoffset'];
$Original['avdataend'] = $info['avdataend'];
@ -357,6 +363,7 @@ class getid3_riff extends getid3_handler {
}
$thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
$thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
$thisfile_riff['comments']['tag_text'][] = substr($thisfile_riff_WAVE_cart_0['data'], 1772);
$thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
$thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
@ -405,7 +412,6 @@ class getid3_riff extends getid3_handler {
'tracktitle'=>'title',
'category' =>'genre',
'cdtitle' =>'album',
'tracktitle'=>'title',
);
foreach ($tagmapping as $fromkey => $tokey) {
if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
@ -613,6 +619,8 @@ class getid3_riff extends getid3_handler {
$thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
$thisfile_video['dataformat'] = 'avi';
$thisfile_riff_video_current = array();
if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
if (isset($thisfile_riff['AVIX'])) {
@ -695,12 +703,13 @@ class getid3_riff extends getid3_handler {
'capturedfile' => 0x00010000,
'copyrighted' => 0x00020010,
);
foreach ($flags as $flag => $value) {
foreach ($flags as $flag => $value) {
$thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
}
// shortcut
$thisfile_riff_video[$streamindex] = array();
/** @var array $thisfile_riff_video_current */
$thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
@ -867,7 +876,7 @@ class getid3_riff extends getid3_handler {
}
}
if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
if (isset($thisfile_riff_raw_strf_strhfccType_streamindex) && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
$thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
if (self::fourccLookup($thisfile_video['fourcc'])) {
@ -914,7 +923,7 @@ class getid3_riff extends getid3_handler {
// http://en.wikipedia.org/wiki/CD-DA
case 'CDDA':
$info['fileformat'] = 'cda';
unset($info['mime_type']);
unset($info['mime_type']);
$thisfile_audio_dataformat = 'cda';
@ -934,7 +943,7 @@ class getid3_riff extends getid3_handler {
$thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
$thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
$info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
$info['comments']['track_number'] = $thisfile_riff_CDDA_fmt_0['track_num'];
$info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
// hardcoded data for CD-audio
@ -947,7 +956,7 @@ class getid3_riff extends getid3_handler {
}
break;
// http://en.wikipedia.org/wiki/AIFF
// http://en.wikipedia.org/wiki/AIFF
case 'AIFF':
case 'AIFC':
$info['fileformat'] = 'aiff';
@ -1057,7 +1066,7 @@ class getid3_riff extends getid3_handler {
if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
@ -1077,6 +1086,7 @@ class getid3_riff extends getid3_handler {
$thisfile_audio_dataformat = '8svx';
$thisfile_audio['bits_per_sample'] = 8;
$thisfile_audio['channels'] = 1; // overridden below, if need be
$ActualBitsPerSample = 0;
if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
@ -1114,7 +1124,7 @@ class getid3_riff extends getid3_handler {
break;
default:
$this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"');
$this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.$thisfile_riff_RIFFsubtype_VHDR_0['sCompression'].'"');
break;
}
}
@ -1159,7 +1169,7 @@ class getid3_riff extends getid3_handler {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_mpeg = new getid3_mpeg($getid3_temp);
$getid3_mpeg->Analyze();
if (empty($getid3_temp->info['error'])) {
@ -1245,7 +1255,7 @@ class getid3_riff extends getid3_handler {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
@ -1372,6 +1382,15 @@ class getid3_riff extends getid3_handler {
return true;
}
/**
* @param int $startoffset
* @param int $maxoffset
*
* @return array|false
*
* @throws Exception
* @throws getid3_exception
*/
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
@ -1480,7 +1499,13 @@ class getid3_riff extends getid3_handler {
return $RIFFchunk;
}
/**
* @param int $startoffset
* @param int $maxoffset
*
* @return array|false
* @throws getid3_exception
*/
public function ParseRIFF($startoffset, $maxoffset) {
$info = &$this->getid3->info;
@ -1529,7 +1554,7 @@ class getid3_riff extends getid3_handler {
// MP3
if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
$getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
@ -1551,7 +1576,7 @@ class getid3_riff extends getid3_handler {
// AC3
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
$getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
$getid3_ac3 = new getid3_ac3($getid3_temp);
@ -1612,7 +1637,7 @@ class getid3_riff extends getid3_handler {
// Probably is MP3 data
if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
@ -1629,7 +1654,7 @@ class getid3_riff extends getid3_handler {
// This is probably AC-3 data
$getid3_temp = new getID3();
if ($isRegularAC3) {
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
}
@ -1663,7 +1688,7 @@ class getid3_riff extends getid3_handler {
// This is probably DTS data
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_dts = new getid3_dts($getid3_temp);
$getid3_dts->Analyze();
@ -1731,8 +1756,77 @@ class getid3_riff extends getid3_handler {
// $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
// break;
case 'scot':
// https://cmsdk.com/node-js/adding-scot-chunk-to-wav-file.html
$RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
$RIFFchunk[$chunkname][$thisindex]['parsed']['alter'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 0, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['attrib'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 1, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['artnum'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 2, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['title'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 4, 43); // "name" in other documentation
$RIFFchunk[$chunkname][$thisindex]['parsed']['copy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 47, 4);
$RIFFchunk[$chunkname][$thisindex]['parsed']['padd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 51, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['asclen'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 52, 5);
$RIFFchunk[$chunkname][$thisindex]['parsed']['startseconds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 57, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['starthundredths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 59, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['endseconds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 61, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['endhundreths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 63, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['sdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 65, 6);
$RIFFchunk[$chunkname][$thisindex]['parsed']['kdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 71, 6);
$RIFFchunk[$chunkname][$thisindex]['parsed']['start_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 77, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['kill_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 78, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['digital'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 79, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 80, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['stereo'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 82, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['compress'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 83, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['eomstrt'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 84, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['eomlen'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 88, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['attrib2'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 90, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['future1'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 94, 12);
$RIFFchunk[$chunkname][$thisindex]['parsed']['catfontcolor'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 106, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['catcolor'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 110, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['segeompos'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 114, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['vt_startsecs'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 118, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['vt_starthunds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 120, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['priorcat'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 122, 3);
$RIFFchunk[$chunkname][$thisindex]['parsed']['priorcopy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 125, 4);
$RIFFchunk[$chunkname][$thisindex]['parsed']['priorpadd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 129, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['postcat'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 130, 3);
$RIFFchunk[$chunkname][$thisindex]['parsed']['postcopy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 133, 4);
$RIFFchunk[$chunkname][$thisindex]['parsed']['postpadd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 137, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['hrcanplay'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 138, 21);
$RIFFchunk[$chunkname][$thisindex]['parsed']['future2'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 159, 108);
$RIFFchunk[$chunkname][$thisindex]['parsed']['artist'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 267, 34);
$RIFFchunk[$chunkname][$thisindex]['parsed']['comment'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 301, 34); // "trivia" in other documentation
$RIFFchunk[$chunkname][$thisindex]['parsed']['intro'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 335, 2);
$RIFFchunk[$chunkname][$thisindex]['parsed']['end'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 337, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['year'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 338, 4);
$RIFFchunk[$chunkname][$thisindex]['parsed']['obsolete2'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 342, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['rec_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 343, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['rdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 344, 6);
$RIFFchunk[$chunkname][$thisindex]['parsed']['mpeg_bitrate'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 350, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['pitch'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 352, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['playlevel'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 354, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['lenvalid'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 356, 1);
$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 357, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['newplaylevel'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 361, 2));
$RIFFchunk[$chunkname][$thisindex]['parsed']['chopsize'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 363, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['vteomovr'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 367, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['desiredlen'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 371, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['triggers'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 375, 4));
$RIFFchunk[$chunkname][$thisindex]['parsed']['fillout'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 379, 33);
foreach (array('title', 'artist', 'comment') as $key) {
if (trim($RIFFchunk[$chunkname][$thisindex]['parsed'][$key])) {
$info['riff']['comments'][$key] = array($RIFFchunk[$chunkname][$thisindex]['parsed'][$key]);
}
}
if ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] && !empty($info['filesize']) && ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] != $info['filesize'])) {
$this->warning('RIFF.WAVE.scot.filelength ('.$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'].') different from actual filesize ('.$info['filesize'].')');
}
break;
default:
if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
if (!empty($LISTchunkParent) && isset($LISTchunkMaxOffset) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
unset($RIFFchunk[$chunkname][$thisindex]['offset']);
@ -1767,6 +1861,11 @@ class getid3_riff extends getid3_handler {
return $RIFFchunk;
}
/**
* @param string $RIFFdata
*
* @return bool
*/
public function ParseRIFFdata(&$RIFFdata) {
$info = &$this->getid3->info;
if ($RIFFdata) {
@ -1804,6 +1903,12 @@ class getid3_riff extends getid3_handler {
return false;
}
/**
* @param array $RIFFinfoArray
* @param array $CommentsTargetArray
*
* @return bool
*/
public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
$RIFFinfoKeyLookup = array(
'IARL'=>'archivallocation',
@ -1863,8 +1968,14 @@ class getid3_riff extends getid3_handler {
return true;
}
/**
* @param string $WaveFormatExData
*
* @return array
*/
public static function parseWAVEFORMATex($WaveFormatExData) {
// shortcut
$WaveFormatEx = array();
$WaveFormatEx['raw'] = array();
$WaveFormatEx_raw = &$WaveFormatEx['raw'];
@ -1888,6 +1999,11 @@ class getid3_riff extends getid3_handler {
return $WaveFormatEx;
}
/**
* @param string $WavPackChunkData
*
* @return bool
*/
public function parseWavPackHeader($WavPackChunkData) {
// typedef struct {
// char ckID [4];
@ -1949,6 +2065,12 @@ class getid3_riff extends getid3_handler {
return true;
}
/**
* @param string $BITMAPINFOHEADER
* @param bool $littleEndian
*
* @return array
*/
public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
$parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
@ -1968,6 +2090,12 @@ class getid3_riff extends getid3_handler {
return $parsed;
}
/**
* @param string $DIVXTAG
* @param bool $raw
*
* @return array
*/
public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
// structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
// source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
@ -2014,6 +2142,7 @@ class getid3_riff extends getid3_handler {
5 => 'NC-17',
);
$parsed = array();
$parsed['title'] = trim(substr($DIVXTAG, 0, 32));
$parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
$parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
@ -2029,8 +2158,8 @@ class getid3_riff extends getid3_handler {
if (!$raw) {
unset($parsed['genre_id'], $parsed['rating_id']);
foreach ($parsed as $key => $value) {
if (!$value === '') {
unset($parsed['key']);
if (empty($value)) {
unset($parsed[$key]);
}
}
}
@ -2042,6 +2171,11 @@ class getid3_riff extends getid3_handler {
return $parsed;
}
/**
* @param string $tagshortname
*
* @return string
*/
public static function waveSNDMtagLookup($tagshortname) {
$begin = __LINE__;
@ -2065,6 +2199,11 @@ class getid3_riff extends getid3_handler {
return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
}
/**
* @param int $wFormatTag
*
* @return string
*/
public static function wFormatTagLookup($wFormatTag) {
$begin = __LINE__;
@ -2234,6 +2373,11 @@ class getid3_riff extends getid3_handler {
return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
}
/**
* @param string $fourcc
*
* @return string
*/
public static function fourccLookup($fourcc) {
$begin = __LINE__;
@ -2628,6 +2772,12 @@ class getid3_riff extends getid3_handler {
return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
}
/**
* @param string $byteword
* @param bool $signed
*
* @return int|float|false
*/
private function EitherEndian2Int($byteword, $signed=false) {
if ($this->container == 'riff') {
return getid3_lib::LittleEndian2Int($byteword, $signed);

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.ac3.php //
@ -17,11 +17,21 @@
class getid3_ac3 extends getid3_handler
{
private $AC3header = array();
private $BSIoffset = 0;
/**
* @var array
*/
private $AC3header = array();
const syncword = 0x0B77;
/**
* @var int
*/
private $BSIoffset = 0;
const syncword = 0x0B77;
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -187,7 +197,7 @@ class getid3_ac3 extends getid3_handler
} elseif ($thisfile_ac3_raw_bsi['bsid'] <= 16) { // E-AC3
$this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.');
$this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.');
$info['audio']['dataformat'] = 'eac3';
$thisfile_ac3_raw_bsi['strmtyp'] = $this->readHeaderBSI(2);
@ -412,7 +422,7 @@ $this->error('E-AC3 parsing is incomplete and experimental in this version of ge
} else {
$this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 16. Please submit a support ticket with a sample file.');
unset($info['ac3']);
unset($info['ac3']);
return false;
}
@ -431,15 +441,17 @@ $this->error('E-AC3 parsing is incomplete and experimental in this version of ge
$thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw_bsi['frmsizecod'], $thisfile_ac3_raw_bsi['fscod']);
$thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw_bsi['frmsizecod']);
} elseif (!empty($thisfile_ac3_raw_bsi['frmsiz'])) {
// this isn't right, but it's (usually) close, roughly 5% less than it should be.
// but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know!
// this isn't right, but it's (usually) close, roughly 5% less than it should be.
// but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know!
$thisfile_ac3['bitrate'] = ($thisfile_ac3_raw_bsi['frmsiz'] + 1) * 16 * 30; // The frmsiz field shall contain a value one less than the overall size of the coded syncframe in 16-bit words. That is, this field may assume a value ranging from 0 to 2047, and these values correspond to syncframe sizes ranging from 1 to 2048.
// kludge-fix to make it approximately the expected value, still not "right":
$thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000;
// kludge-fix to make it approximately the expected value, still not "right":
$thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000;
}
$info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
$thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
if (isset($thisfile_ac3_raw_bsi['bsmod']) && isset($thisfile_ac3_raw_bsi['acmod'])) {
$thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
}
$ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
foreach($ac3_coding_mode as $key => $value) {
$thisfile_ac3[$key] = $value;
@ -470,6 +482,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return true;
}
/**
* @param int $length
*
* @return float|int
*/
private function readHeaderBSI($length) {
$data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
$this->BSIoffset += $length;
@ -477,6 +494,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return bindec($data);
}
/**
* @param int $fscod
*
* @return int|string|false
*/
public static function sampleRateCodeLookup($fscod) {
static $sampleRateCodeLookup = array(
0 => 48000,
@ -487,6 +509,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
}
/**
* @param int $fscod2
*
* @return int|string|false
*/
public static function sampleRateCodeLookup2($fscod2) {
static $sampleRateCodeLookup2 = array(
0 => 24000,
@ -497,6 +524,12 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($sampleRateCodeLookup2[$fscod2]) ? $sampleRateCodeLookup2[$fscod2] : false);
}
/**
* @param int $bsmod
* @param int $acmod
*
* @return string|false
*/
public static function serviceTypeLookup($bsmod, $acmod) {
static $serviceTypeLookup = array();
if (empty($serviceTypeLookup)) {
@ -518,6 +551,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false);
}
/**
* @param int $acmod
*
* @return array|false
*/
public static function audioCodingModeLookup($acmod) {
// array(channel configuration, # channels (not incl LFE), channel order)
static $audioCodingModeLookup = array (
@ -533,6 +571,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
}
/**
* @param int $cmixlev
*
* @return int|float|string|false
*/
public static function centerMixLevelLookup($cmixlev) {
static $centerMixLevelLookup;
if (empty($centerMixLevelLookup)) {
@ -546,6 +589,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false);
}
/**
* @param int $surmixlev
*
* @return int|float|string|false
*/
public static function surroundMixLevelLookup($surmixlev) {
static $surroundMixLevelLookup;
if (empty($surroundMixLevelLookup)) {
@ -559,6 +607,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false);
}
/**
* @param int $dsurmod
*
* @return string|false
*/
public static function dolbySurroundModeLookup($dsurmod) {
static $dolbySurroundModeLookup = array(
0 => 'not indicated',
@ -569,12 +622,18 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false);
}
/**
* @param int $acmod
* @param bool $lfeon
*
* @return array
*/
public static function channelsEnabledLookup($acmod, $lfeon) {
$lookup = array(
'ch1'=>(bool) ($acmod == 0),
'ch2'=>(bool) ($acmod == 0),
'left'=>(bool) ($acmod > 1),
'right'=>(bool) ($acmod > 1),
'ch1'=>($acmod == 0),
'ch2'=>($acmod == 0),
'left'=>($acmod > 1),
'right'=>($acmod > 1),
'center'=>(bool) ($acmod & 0x01),
'surround_mono'=>false,
'surround_left'=>false,
@ -594,6 +653,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return $lookup;
}
/**
* @param int $compre
*
* @return float|int
*/
public static function heavyCompression($compre) {
// The first four bits indicate gain changes in 6.02dB increments which can be
// implemented with an arithmetic shift operation. The following four bits
@ -623,7 +687,7 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
// -8 -42.14 dB
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
if ($fourbit{0} == '1') {
if ($fourbit[0] == '1') {
$log_gain = -8 + bindec(substr($fourbit, 1));
} else {
$log_gain = bindec(substr($fourbit, 1));
@ -644,6 +708,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return $log_gain - $lin_gain;
}
/**
* @param int $roomtyp
*
* @return string|false
*/
public static function roomTypeLookup($roomtyp) {
static $roomTypeLookup = array(
0 => 'not indicated',
@ -654,6 +723,12 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false);
}
/**
* @param int $frmsizecod
* @param int $fscod
*
* @return int|false
*/
public static function frameSizeLookup($frmsizecod, $fscod) {
// LSB is whether padding is used or not
$padding = (bool) ($frmsizecod & 0x01);
@ -683,13 +758,20 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
18 => array(2560, 2786, 3840) // 640 kbps
);
}
$paddingBytes = 0;
if (($fscod == 1) && $padding) {
// frame lengths are padded by 1 word (16 bits) at 44100
$frameSizeLookup[$frmsizecod] += 2;
// (fscode==1) means 44100Hz (see sampleRateCodeLookup)
$paddingBytes = 2;
}
return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] + $paddingBytes : false);
}
/**
* @param int $frmsizecod
*
* @return int|false
*/
public static function bitrateLookup($frmsizecod) {
// LSB is whether padding is used or not
$padding = (bool) ($frmsizecod & 0x01);
@ -719,6 +801,11 @@ $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16
return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
}
/**
* @param int $numblkscod
*
* @return int|false
*/
public static function blocksPerSyncFrame($numblkscod) {
static $blocksPerSyncFrameLookup = array(
0 => 1,

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.dts.php //
@ -21,21 +21,27 @@
class getid3_dts extends getid3_handler
{
/**
* Default DTS syncword used in native .cpt or .dts formats
*/
const syncword = "\x7F\xFE\x80\x01";
* Default DTS syncword used in native .cpt or .dts formats.
*/
const syncword = "\x7F\xFE\x80\x01";
/**
* @var int
*/
private $readBinDataOffset = 0;
/**
* Possible syncwords indicating bitstream encoding
*/
public static $syncwords = array(
0 => "\x7F\xFE\x80\x01", // raw big-endian
1 => "\xFE\x7F\x01\x80", // raw little-endian
2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
/**
* Possible syncwords indicating bitstream encoding.
*/
public static $syncwords = array(
0 => "\x7F\xFE\x80\x01", // raw big-endian
1 => "\xFE\x7F\x01\x80", // raw little-endian
2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
$info['fileformat'] = 'dts';
@ -45,18 +51,18 @@ class getid3_dts extends getid3_handler
// check syncword
$sync = substr($DTSheader, 0, 4);
if (($encoding = array_search($sync, self::$syncwords)) !== false) {
if (($encoding = array_search($sync, self::$syncwords)) !== false) {
$info['dts']['raw']['magic'] = $sync;
$info['dts']['raw']['magic'] = $sync;
$this->readBinDataOffset = 32;
} elseif ($this->isDependencyFor('matroska')) {
} elseif ($this->isDependencyFor('matroska')) {
// Matroska contains DTS without syncword encoded as raw big-endian format
$encoding = 0;
$this->readBinDataOffset = 0;
} else {
} else {
unset($info['fileformat']);
return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
@ -139,6 +145,12 @@ class getid3_dts extends getid3_handler
return true;
}
/**
* @param string $bin
* @param int $length
*
* @return float|int
*/
private function readBinData($bin, $length) {
$data = substr($bin, $this->readBinDataOffset, $length);
$this->readBinDataOffset += $length;
@ -146,6 +158,11 @@ class getid3_dts extends getid3_handler
return bindec($data);
}
/**
* @param int $index
*
* @return int|string|false
*/
public static function bitrateLookup($index) {
static $lookup = array(
0 => 32000,
@ -184,6 +201,11 @@ class getid3_dts extends getid3_handler
return (isset($lookup[$index]) ? $lookup[$index] : false);
}
/**
* @param int $index
*
* @return int|string|false
*/
public static function sampleRateLookup($index) {
static $lookup = array(
0 => 'invalid',
@ -206,6 +228,11 @@ class getid3_dts extends getid3_handler
return (isset($lookup[$index]) ? $lookup[$index] : false);
}
/**
* @param int $index
*
* @return int|false
*/
public static function bitPerSampleLookup($index) {
static $lookup = array(
0 => 16,
@ -216,6 +243,11 @@ class getid3_dts extends getid3_handler
return (isset($lookup[$index]) ? $lookup[$index] : false);
}
/**
* @param int $index
*
* @return int|false
*/
public static function numChannelsLookup($index) {
switch ($index) {
case 0:
@ -254,6 +286,11 @@ class getid3_dts extends getid3_handler
return false;
}
/**
* @param int $index
*
* @return string
*/
public static function channelArrangementLookup($index) {
static $lookup = array(
0 => 'A',
@ -276,6 +313,12 @@ class getid3_dts extends getid3_handler
return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
}
/**
* @param int $index
* @param int $version
*
* @return int|false
*/
public static function dialogNormalization($index, $version) {
switch ($version) {
case 7:

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.flac.php //
@ -24,6 +24,9 @@ class getid3_flac extends getid3_handler
{
const syncword = 'fLaC';
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -41,22 +44,30 @@ class getid3_flac extends getid3_handler
return $this->parseMETAdata();
}
/**
* @return bool
*/
public function parseMETAdata() {
$info = &$this->getid3->info;
do {
$BlockOffset = $this->ftell();
$BlockHeader = $this->fread(4);
$LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
$LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1)); // LBFBT = LastBlockFlag + BlockType
$LastBlockFlag = (bool) ($LBFBT & 0x80);
$BlockType = ($LBFBT & 0x7F);
$BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
$BlockTypeText = self::metaBlockTypeLookup($BlockType);
if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
$this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
$this->warning('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
break;
}
if ($BlockLength < 1) {
if ($BlockTypeText != 'reserved') {
// probably supposed to be zero-length
$this->warning('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockTypeText.') at offset '.$BlockOffset.' is zero bytes');
continue;
}
$this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
break;
}
@ -167,7 +178,7 @@ class getid3_flac extends getid3_handler
if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
}
else {
$info['md5_data_source'] = '';
@ -194,12 +205,14 @@ class getid3_flac extends getid3_handler
return true;
}
private function parseSTREAMINFO($BlockData) {
$info = &$this->getid3->info;
$info['flac']['STREAMINFO'] = array();
$streaminfo = &$info['flac']['STREAMINFO'];
/**
* @param string $BlockData
*
* @return array
*/
public static function parseSTREAMINFOdata($BlockData) {
$streaminfo = array();
$streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
$streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
$streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
@ -211,15 +224,28 @@ class getid3_flac extends getid3_handler
$streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
$streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
$streaminfo['audio_signature'] = substr($BlockData, 18, 16);
$streaminfo['audio_signature'] = substr($BlockData, 18, 16);
if (!empty($streaminfo['sample_rate'])) {
return $streaminfo;
}
/**
* @param string $BlockData
*
* @return bool
*/
private function parseSTREAMINFO($BlockData) {
$info = &$this->getid3->info;
$info['flac']['STREAMINFO'] = self::parseSTREAMINFOdata($BlockData);
if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['sample_rate'] = $streaminfo['sample_rate'];
$info['audio']['channels'] = $streaminfo['channels'];
$info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
$info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
$info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate'];
$info['audio']['channels'] = $info['flac']['STREAMINFO']['channels'];
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
$info['playtime_seconds'] = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
if ($info['playtime_seconds'] > 0) {
if (!$this->isDependencyFor('matroska')) {
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
@ -236,6 +262,11 @@ class getid3_flac extends getid3_handler
return true;
}
/**
* @param string $BlockData
*
* @return bool
*/
private function parseAPPLICATION($BlockData) {
$info = &$this->getid3->info;
@ -246,6 +277,11 @@ class getid3_flac extends getid3_handler
return true;
}
/**
* @param string $BlockData
*
* @return bool
*/
private function parseSEEKTABLE($BlockData) {
$info = &$this->getid3->info;
@ -275,6 +311,11 @@ class getid3_flac extends getid3_handler
return true;
}
/**
* @param string $BlockData
*
* @return bool
*/
private function parseVORBIS_COMMENT($BlockData) {
$info = &$this->getid3->info;
@ -294,6 +335,11 @@ class getid3_flac extends getid3_handler
return true;
}
/**
* @param string $BlockData
*
* @return bool
*/
private function parseCUESHEET($BlockData) {
$info = &$this->getid3->info;
$offset = 0;
@ -346,9 +392,11 @@ class getid3_flac extends getid3_handler
}
/**
* Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
* External usage: audio.ogg
*/
* Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
* External usage: audio.ogg
*
* @return bool
*/
public function parsePICTURE() {
$info = &$this->getid3->info;
@ -380,6 +428,11 @@ class getid3_flac extends getid3_handler
return true;
}
/**
* @param int $blocktype
*
* @return string
*/
public static function metaBlockTypeLookup($blocktype) {
static $lookup = array(
0 => 'STREAMINFO',
@ -393,6 +446,11 @@ class getid3_flac extends getid3_handler
return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
}
/**
* @param int $applicationid
*
* @return string
*/
public static function applicationIDLookup($applicationid) {
// http://flac.sourceforge.net/id.html
static $lookup = array(
@ -423,6 +481,11 @@ class getid3_flac extends getid3_handler
return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
}
/**
* @param int $type_id
*
* @return string
*/
public static function pictureTypeLookup($type_id) {
static $lookup = array (
0 => 'Other',

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.mp3.php //
@ -24,9 +24,17 @@ define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
class getid3_mp3 extends getid3_handler
{
/**
* Forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow,
* unrecommended, but may provide data from otherwise-unusable files.
*
* @var bool
*/
public $allow_bruteforce = false;
public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -35,7 +43,7 @@ class getid3_mp3 extends getid3_handler
if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
if ($this->allow_bruteforce) {
$this->error('Rescanning file in BruteForce mode');
$this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
$this->getOnlyMPEGaudioInfoBruteForce();
}
}
@ -152,7 +160,11 @@ class getid3_mp3 extends getid3_handler
// Calculate playtime
if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
// https://github.com/JamesHeinrich/getID3/issues/161
// VBR header frame contains ~0.026s of silent audio data, but is not actually part of the original encoding and should be ignored
$xingVBRheaderFrameLength = ((isset($info['mpeg']['audio']['VBR_frames']) && isset($info['mpeg']['audio']['framelength'])) ? $info['mpeg']['audio']['framelength'] : 0);
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset'] - $xingVBRheaderFrameLength) * 8 / $info['audio']['bitrate'];
}
$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
@ -160,10 +172,14 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @return string
*/
public function GuessEncoderOptions() {
// shortcuts
$info = &$this->getid3->info;
$thisfile_mpeg_audio = array();
$thisfile_mpeg_audio_lame = array();
if (!empty($info['mpeg']['audio'])) {
$thisfile_mpeg_audio = &$info['mpeg']['audio'];
if (!empty($thisfile_mpeg_audio['LAME'])) {
@ -178,7 +194,7 @@ class getid3_mp3 extends getid3_handler
$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && isset($thisfile_mpeg_audio_lame['preset_used_id']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
@ -404,7 +420,15 @@ class getid3_mp3 extends getid3_handler
return $encoder_options;
}
/**
* @param int $offset
* @param array $info
* @param bool $recursivesearch
* @param bool $ScanAsCBR
* @param bool $FastMPEGheaderScan
*
* @return bool
*/
public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
static $MPEGaudioVersionLookup;
static $MPEGaudioLayerLookup;
@ -458,7 +482,6 @@ class getid3_mp3 extends getid3_handler
}
$thisfile_mpeg_audio = &$info['mpeg']['audio'];
if ($MPEGaudioHeaderValidCache[$head4_key]) {
$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
} else {
@ -562,7 +585,7 @@ class getid3_mp3 extends getid3_handler
$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
$thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
$info['audio']['codec'] = 'Fraunhofer';
$info['audio']['codec'] = 'Fraunhofer';
$SideInfoData = substr($headerstring, 4 + 2, 32);
@ -655,7 +678,7 @@ class getid3_mp3 extends getid3_handler
$used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
} elseif (!empty($info['filesize'])) {
$used_filesize = $info['filesize'];
$used_filesize -= intval(@$info['id3v2']['headerlength']);
$used_filesize -= (isset($info['id3v2']['headerlength']) ? intval($info['id3v2']['headerlength']) : 0);
$used_filesize -= (isset($info['id3v1']) ? 128 : 0);
$used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
$this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes');
@ -678,7 +701,7 @@ class getid3_mp3 extends getid3_handler
if ($thisfile_mpeg_audio['xing_flags']['toc']) {
$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
for ($i = 0; $i < 100; $i++) {
$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
}
}
if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
@ -1083,6 +1106,13 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @param int $offset
* @param int $nextframetestoffset
* @param bool $ScanAsCBR
*
* @return bool
*/
public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
$info = &$this->getid3->info;
$firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
@ -1129,6 +1159,12 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @param int $offset
* @param bool $deepscan
*
* @return int|false
*/
public function FreeFormatFrameLength($offset, $deepscan=false) {
$info = &$this->getid3->info;
@ -1137,9 +1173,9 @@ class getid3_mp3 extends getid3_handler
$SyncPattern1 = substr($MPEGaudioData, 0, 4);
// may be different pattern due to padding
$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
if ($SyncPattern2 === $SyncPattern1) {
$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
}
$framelength = false;
@ -1206,6 +1242,9 @@ class getid3_mp3 extends getid3_handler
return $framelength;
}
/**
* @return bool
*/
public function getOnlyMPEGaudioInfoBruteForce() {
$MPEGaudioHeaderDecodeCache = array();
$MPEGaudioHeaderValidCache = array();
@ -1241,9 +1280,9 @@ class getid3_mp3 extends getid3_handler
if (strlen($head4) < 4) {
break;
}
if ($head4{0} != "\xFF") {
if ($head4[0] != "\xFF") {
for ($i = 1; $i < 4; $i++) {
if ($head4{$i} == "\xFF") {
if ($head4[$i] == "\xFF") {
$this->fseek($i - 4, SEEK_CUR);
continue 2;
}
@ -1275,7 +1314,7 @@ class getid3_mp3 extends getid3_handler
$WhereWeWere = $this->ftell();
$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
$next4 = $this->fread(4);
if ($next4{0} == "\xFF") {
if ($next4[0] == "\xFF") {
if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
}
@ -1353,7 +1392,12 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @param int $avdataoffset
* @param bool $BitrateHistogram
*
* @return bool
*/
public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
// looks for synch, decodes MPEG audio header
@ -1363,10 +1407,9 @@ class getid3_mp3 extends getid3_handler
static $MPEGaudioLayerLookup;
static $MPEGaudioBitrateLookup;
if (empty($MPEGaudioVersionLookup)) {
$MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
$MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
}
$this->fseek($avdataoffset);
@ -1416,7 +1459,8 @@ class getid3_mp3 extends getid3_handler
return false;
}
if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected
$FirstFrameAVDataOffset = null;
if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
$FirstFrameThisfileInfo = $info;
$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
@ -1440,7 +1484,7 @@ class getid3_mp3 extends getid3_handler
$info['audio']['dataformat'] = 'mp3';
break;
}
if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
// If there is garbage data between a valid VBR header frame and a sequence
// of valid MPEG-audio frames the VBR data is no longer discarded.
@ -1510,7 +1554,7 @@ class getid3_mp3 extends getid3_handler
$this->fseek($scan_start_offset[$current_segment]);
$buffer_4k = $this->fread(4096);
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)) {
$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
@ -1522,7 +1566,7 @@ class getid3_mp3 extends getid3_handler
}
}
$synchstartoffset = $scan_start_offset[$current_segment];
while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
$FastMode = true;
$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
@ -1633,17 +1677,25 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @return array
*/
public static function MPEGaudioVersionArray() {
static $MPEGaudioVersion = array('2.5', false, '2', '1');
return $MPEGaudioVersion;
}
/**
* @return array
*/
public static function MPEGaudioLayerArray() {
static $MPEGaudioLayer = array(false, 3, 2, 1);
return $MPEGaudioLayer;
}
/**
* @return array
*/
public static function MPEGaudioBitrateArray() {
static $MPEGaudioBitrate;
if (empty($MPEGaudioBitrate)) {
@ -1663,6 +1715,9 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioBitrate;
}
/**
* @return array
*/
public static function MPEGaudioFrequencyArray() {
static $MPEGaudioFrequency;
if (empty($MPEGaudioFrequency)) {
@ -1675,11 +1730,17 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioFrequency;
}
/**
* @return array
*/
public static function MPEGaudioChannelModeArray() {
static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
return $MPEGaudioChannelMode;
}
/**
* @return array
*/
public static function MPEGaudioModeExtensionArray() {
static $MPEGaudioModeExtension;
if (empty($MPEGaudioModeExtension)) {
@ -1692,15 +1753,31 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioModeExtension;
}
/**
* @return array
*/
public static function MPEGaudioEmphasisArray() {
static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
return $MPEGaudioEmphasis;
}
/**
* @param string $head4
* @param bool $allowBitrate15
*
* @return bool
*/
public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
}
/**
* @param array $rawarray
* @param bool $echoerrors
* @param bool $allowBitrate15
*
* @return bool
*/
public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
return false;
@ -1773,6 +1850,11 @@ class getid3_mp3 extends getid3_handler
return true;
}
/**
* @param string $Header4Bytes
*
* @return array|false
*/
public static function MPEGaudioHeaderDecode($Header4Bytes) {
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
// A - Frame sync (all bits set)
@ -1794,22 +1876,31 @@ class getid3_mp3 extends getid3_handler
}
$MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
$MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
$MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
$MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
$MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
$MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
$MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
$MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
$MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
$MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
$MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
$MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
$MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3; // BB
$MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x06) >> 1; // CC
$MPEGrawHeader['protection'] = (ord($Header4Bytes[1]) & 0x01); // D
$MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
$MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0x0C) >> 2; // FF
$MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x02) >> 1; // G
$MPEGrawHeader['private'] = (ord($Header4Bytes[2]) & 0x01); // H
$MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
$MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; // JJ
$MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x08) >> 3; // K
$MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x04) >> 2; // L
$MPEGrawHeader['emphasis'] = (ord($Header4Bytes[3]) & 0x03); // MM
return $MPEGrawHeader;
}
/**
* @param int|string $bitrate
* @param string $version
* @param string $layer
* @param bool $padding
* @param int $samplerate
*
* @return int|false
*/
public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
static $AudioFrameLengthCache = array();
@ -1871,6 +1962,11 @@ class getid3_mp3 extends getid3_handler
return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
}
/**
* @param float|int $bit_rate
*
* @return int|float|string
*/
public static function ClosestStandardMP3Bitrate($bit_rate) {
static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
static $bit_rate_table = array (0=>'-');
@ -1891,10 +1987,16 @@ class getid3_mp3 extends getid3_handler
return $bit_rate_table[$round_bit_rate];
}
/**
* @param string $version
* @param string $channelmode
*
* @return int
*/
public static function XingVBRidOffset($version, $channelmode) {
static $XingVBRidOffsetCache = array();
if (empty($XingVBRidOffset)) {
$XingVBRidOffset = array (
if (empty($XingVBRidOffsetCache)) {
$XingVBRidOffsetCache = array (
'1' => array ('mono' => 0x15, // 4 + 17 = 21
'stereo' => 0x24, // 4 + 32 = 36
'joint stereo' => 0x24,
@ -1914,9 +2016,14 @@ class getid3_mp3 extends getid3_handler
)
);
}
return $XingVBRidOffset[$version][$channelmode];
return $XingVBRidOffsetCache[$version][$channelmode];
}
/**
* @param int $VBRmethodID
*
* @return string
*/
public static function LAMEvbrMethodLookup($VBRmethodID) {
static $LAMEvbrMethodLookup = array(
0x00 => 'unknown',
@ -1933,6 +2040,11 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
}
/**
* @param int $StereoModeID
*
* @return string
*/
public static function LAMEmiscStereoModeLookup($StereoModeID) {
static $LAMEmiscStereoModeLookup = array(
0 => 'mono',
@ -1947,6 +2059,11 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
}
/**
* @param int $SourceSampleFrequencyID
*
* @return string
*/
public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
static $LAMEmiscSourceSampleFrequencyLookup = array(
0 => '<= 32 kHz',
@ -1957,6 +2074,11 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
}
/**
* @param int $SurroundInfoID
*
* @return string
*/
public static function LAMEsurroundInfoLookup($SurroundInfoID) {
static $LAMEsurroundInfoLookup = array(
0 => 'no surround info',
@ -1967,6 +2089,11 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
}
/**
* @param array $LAMEtag
*
* @return string
*/
public static function LAMEpresetUsedLookup($LAMEtag) {
if ($LAMEtag['preset_used_id'] == 0) {

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.ogg.php //
@ -18,7 +18,11 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE
class getid3_ogg extends getid3_handler
{
// http://xiph.org/vorbis/doc/Vorbis_I_spec.html
/**
* @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html
*
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -65,7 +69,7 @@ class getid3_ogg extends getid3_handler
} elseif (substr($filedata, 0, 8) == 'OpusHead') {
if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) {
return false;
}
@ -179,7 +183,7 @@ class getid3_ogg extends getid3_handler
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'];
}
$this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
$this->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") {
@ -259,9 +263,34 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
$this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
//return false;
} elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') {
// https://xiph.org/flac/ogg_mapping.html
$info['audio']['dataformat'] = 'flac';
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = true;
$info['ogg']['flac']['header']['version_major'] = ord(substr($filedata, 5, 1));
$info['ogg']['flac']['header']['version_minor'] = ord(substr($filedata, 6, 1));
$info['ogg']['flac']['header']['header_packets'] = getid3_lib::BigEndian2Int(substr($filedata, 7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams."
$info['ogg']['flac']['header']['magic'] = substr($filedata, 9, 4);
if ($info['ogg']['flac']['header']['magic'] != 'fLaC') {
$this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')');
return false;
}
$info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4));
$info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34));
if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['sample_rate'] = $info['flac']['STREAMINFO']['sample_rate'];
$info['audio']['channels'] = $info['flac']['STREAMINFO']['channels'];
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
$info['playtime_seconds'] = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
}
} else {
$this->error('Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"');
$this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"');
unset($info['ogg']);
unset($info['mime_type']);
return false;
@ -378,6 +407,13 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return true;
}
/**
* @param string $filedata
* @param int $filedataoffset
* @param array $oggpageinfo
*
* @return bool
*/
public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$info = &$this->getid3->info;
$info['audio']['dataformat'] = 'vorbis';
@ -426,7 +462,15 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return true;
}
// http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
/**
* @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
*
* @param string $filedata
* @param int $filedataoffset
* @param array $oggpageinfo
*
* @return bool
*/
public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$info = &$this->getid3->info;
$info['audio']['dataformat'] = 'opus';
@ -458,7 +502,7 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
@ -467,16 +511,19 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
//$filedataoffset += 1;
$info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
$info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate'];
$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
$info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
$info['opus']['sample_rate_input'] = $info['ogg']['pageheader']['opus']['input_sample_rate'];
$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
$info['audio']['channels'] = $info['opus']['out_channel_count'];
$info['audio']['sample_rate'] = $info['opus']['sample_rate'];
$info['audio']['channels'] = $info['opus']['out_channel_count'];
$info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input'];
$info['audio']['sample_rate'] = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html
return true;
}
/**
* @return array|false
*/
public function ParseOggPageHeader() {
// http://xiph.org/ogg/vorbis/doc/framing.html
$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
@ -489,7 +536,7 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return false;
}
if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
// get some more data, unless eof, in which case fail
return false;
}
@ -528,13 +575,19 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return $oggheader;
}
// http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
/**
* @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
*
* @return bool
*/
public function ParseVorbisComments() {
$info = &$this->getid3->info;
$OriginalOffset = $this->ftell();
$commentdata = null;
$commentdataoffset = 0;
$VorbisCommentPage = 1;
$CommentStartOffset = 0;
switch ($info['audio']['dataformat']) {
case 'vorbis':
@ -765,6 +818,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return true;
}
/**
* @param int $mode
*
* @return string|null
*/
public static function SpeexBandModeLookup($mode) {
static $SpeexBandModeLookup = array();
if (empty($SpeexBandModeLookup)) {
@ -775,8 +833,14 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
}
/**
* @param array $OggInfoArray
* @param int $SegmentNumber
*
* @return int
*/
public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
$segmentlength = 0;
for ($i = 0; $i < $SegmentNumber; $i++) {
$segmentlength = 0;
foreach ($OggInfoArray['segment_table'] as $key => $value) {
@ -789,7 +853,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return $segmentlength;
}
/**
* @param int $nominal_bitrate
*
* @return float
*/
public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
// decrease precision
@ -813,6 +881,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return round($qval, 1); // 5 or 4.9
}
/**
* @param int $colorspace_id
*
* @return string|null
*/
public static function TheoraColorSpace($colorspace_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.3)
static $TheoraColorSpaceLookup = array();
@ -825,6 +898,11 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['
return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
}
/**
* @param int $pixelformat_id
*
* @return string|null
*/
public static function TheoraPixelFormat($pixelformat_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.4)
static $TheoraPixelFormatLookup = array();

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.tag.apetag.php //
@ -16,9 +16,21 @@
class getid3_apetag extends getid3_handler
{
public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
/**
* true: return full data for all attachments;
* false: return no data for all attachments;
* integer: return data for attachments <= than this;
* string: save as file to this directory.
*
* @var int|bool|string
*/
public $inline_attachments = true;
public $overrideendoffset = 0;
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -150,8 +162,8 @@ class getid3_apetag extends getid3_handler
switch (strtolower($item_key)) {
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
case 'replaygain_track_gain':
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
} else {
$this->warning('MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
@ -159,8 +171,8 @@ class getid3_apetag extends getid3_handler
break;
case 'replaygain_track_peak':
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
if ($thisfile_replaygain['track']['peak'] <= 0) {
$this->warning('ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
@ -171,8 +183,8 @@ class getid3_apetag extends getid3_handler
break;
case 'replaygain_album_gain':
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
} else {
$this->warning('MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
@ -180,8 +192,8 @@ class getid3_apetag extends getid3_handler
break;
case 'replaygain_album_peak':
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
if ($thisfile_replaygain['album']['peak'] <= 0) {
$this->warning('ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
@ -225,7 +237,7 @@ class getid3_apetag extends getid3_handler
case 'tracknumber':
if (is_array($thisfile_ape_items_current['data'])) {
foreach ($thisfile_ape_items_current['data'] as $comment) {
$thisfile_ape['comments']['track'][] = $comment;
$thisfile_ape['comments']['track_number'][] = $comment;
}
}
break;
@ -335,6 +347,11 @@ class getid3_apetag extends getid3_handler
return true;
}
/**
* @param string $APEheaderFooterData
*
* @return array|false
*/
public function parseAPEheaderFooter($APEheaderFooterData) {
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
@ -359,6 +376,11 @@ class getid3_apetag extends getid3_handler
return $headerfooterinfo;
}
/**
* @param int $rawflagint
*
* @return array
*/
public function parseAPEtagFlags($rawflagint) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading."
@ -374,6 +396,11 @@ class getid3_apetag extends getid3_handler
return $flags;
}
/**
* @param int $contenttypeid
*
* @return string
*/
public function APEcontentTypeFlagLookup($contenttypeid) {
static $APEcontentTypeFlagLookup = array(
0 => 'utf-8',
@ -384,6 +411,11 @@ class getid3_apetag extends getid3_handler
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
}
/**
* @param string $itemkey
*
* @return bool
*/
public function APEtagItemIsUTF8Lookup($itemkey) {
static $APEtagItemIsUTF8Lookup = array(
'title',

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.tag.id3v1.php //
@ -17,7 +17,9 @@
class getid3_id3v1 extends getid3_handler
{
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -43,9 +45,9 @@ class getid3_id3v1 extends getid3_handler
// If second-last byte of comment field is null and last byte of comment field is non-null
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
$ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
if (($id3v1tag[125] === "\x00") && ($id3v1tag[126] !== "\x00")) {
$ParsedID3v1['track_number'] = ord(substr($ParsedID3v1['comment'], 29, 1));
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
}
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
@ -66,7 +68,7 @@ class getid3_id3v1 extends getid3_handler
$ID3v1encoding = 'ISO-8859-1';
foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) {
if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
if (preg_match('#^[\\x00-\\x40\\xA8\\xB8\\x80-\\xFF]+$#', $value)) {
foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
$ID3v1encoding = $id3v1_bad_encoding;
@ -89,7 +91,7 @@ class getid3_id3v1 extends getid3_handler
$ParsedID3v1['year'],
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
$ParsedID3v1['comment'],
(!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
(!empty($ParsedID3v1['track_number']) ? $ParsedID3v1['track_number'] : ''));
$ParsedID3v1['padding_valid'] = true;
if ($id3v1tag !== $GoodFormatID3v1tag) {
$ParsedID3v1['padding_valid'] = false;
@ -124,10 +126,20 @@ class getid3_id3v1 extends getid3_handler
return true;
}
/**
* @param string $str
*
* @return string
*/
public static function cutfield($str) {
return trim(substr($str, 0, strcspn($str, "\x00")));
}
/**
* @param bool $allowSCMPXextended
*
* @return string[]
*/
public static function ArrayOfGenres($allowSCMPXextended=false) {
static $GenreLookup = array(
0 => 'Blues',
@ -312,6 +324,12 @@ class getid3_id3v1 extends getid3_handler
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
}
/**
* @param string $genreid
* @param bool $allowSCMPXextended
*
* @return string|false
*/
public static function LookupGenreName($genreid, $allowSCMPXextended=true) {
switch ($genreid) {
case 'RX':
@ -328,6 +346,12 @@ class getid3_id3v1 extends getid3_handler
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
}
/**
* @param string $genre
* @param bool $allowSCMPXextended
*
* @return string|false
*/
public static function LookupGenreID($genre, $allowSCMPXextended=false) {
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
@ -339,6 +363,11 @@ class getid3_id3v1 extends getid3_handler
return false;
}
/**
* @param string $OriginalGenre
*
* @return string|false
*/
public static function StandardiseID3v1GenreName($OriginalGenre) {
if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
return self::LookupGenreName($GenreID);
@ -346,6 +375,17 @@ class getid3_id3v1 extends getid3_handler
return $OriginalGenre;
}
/**
* @param string $title
* @param string $artist
* @param string $album
* @param string $year
* @param int $genreid
* @param string $comment
* @param int|string $track
*
* @return string
*/
public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
$ID3v1Tag = 'TAG';
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
/// //
// module.tag.id3v2.php //
@ -20,6 +20,9 @@ class getid3_id3v2 extends getid3_handler
{
public $StartingOffset = 0;
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -56,8 +59,8 @@ class getid3_id3v2 extends getid3_handler
$header = $this->fread(10);
if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
$thisfile_id3v2['majorversion'] = ord($header{3});
$thisfile_id3v2['minorversion'] = ord($header{4});
$thisfile_id3v2['majorversion'] = ord($header[3]);
$thisfile_id3v2['minorversion'] = ord($header[4]);
// shortcut
$id3v2_majorversion = &$thisfile_id3v2['majorversion'];
@ -76,7 +79,7 @@ class getid3_id3v2 extends getid3_handler
}
$id3_flags = ord($header{5});
$id3_flags = ord($header[5]);
switch ($id3v2_majorversion) {
case 2:
// %ab000000 in v2.2
@ -257,7 +260,7 @@ class getid3_id3v2 extends getid3_handler
$thisfile_id3v2['padding']['length'] = strlen($framedata);
$thisfile_id3v2['padding']['valid'] = true;
for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
if ($framedata{$i} != "\x00") {
if ($framedata[$i] != "\x00") {
$thisfile_id3v2['padding']['valid'] = false;
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
@ -266,6 +269,10 @@ class getid3_id3v2 extends getid3_handler
}
break; // skip rest of ID3v2 header
}
$frame_header = null;
$frame_name = null;
$frame_size = null;
$frame_flags = null;
if ($id3v2_majorversion == 2) {
// Frame ID $xx xx xx (three characters)
// Size $xx xx xx (24-bit integer)
@ -319,7 +326,7 @@ class getid3_id3v2 extends getid3_handler
$len = strlen($framedata);
for ($i = 0; $i < $len; $i++) {
if ($framedata{$i} != "\x00") {
if ($framedata[$i] != "\x00") {
$thisfile_id3v2['padding']['valid'] = false;
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
@ -427,11 +434,11 @@ class getid3_id3v2 extends getid3_handler
$footer = $this->fread(10);
if (substr($footer, 0, 3) == '3DI') {
$thisfile_id3v2['footer'] = true;
$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
$thisfile_id3v2['minorversion_footer'] = ord($footer{4});
$thisfile_id3v2['majorversion_footer'] = ord($footer[3]);
$thisfile_id3v2['minorversion_footer'] = ord($footer[4]);
}
if ($thisfile_id3v2['majorversion_footer'] <= 4) {
$id3_flags = ord(substr($footer{5}));
$id3_flags = ord($footer[5]);
$thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
$thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
$thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
@ -452,10 +459,10 @@ class getid3_id3v2 extends getid3_handler
unset($key, $value, $genres, $genre);
}
if (isset($thisfile_id3v2['comments']['track'])) {
foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
if (isset($thisfile_id3v2['comments']['track_number'])) {
foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) {
if (strstr($value, '/')) {
list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]);
}
}
}
@ -498,7 +505,11 @@ class getid3_id3v2 extends getid3_handler
return true;
}
/**
* @param string $genrestring
*
* @return array
*/
public function ParseID3v2GenreString($genrestring) {
// Parse genres into arrays of genreName and genreID
// ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
@ -530,7 +541,7 @@ class getid3_id3v2 extends getid3_handler
foreach ($genre_elements as $element) {
$element = trim($element);
if ($element) {
if (preg_match('#^[0-9]{1,3}#', $element)) {
if (preg_match('#^[0-9]{1,3}$#', $element)) {
$clean_genres[] = getid3_id3v1::LookupGenreName($element);
} else {
$clean_genres[] = str_replace('((', '(', $element);
@ -540,7 +551,11 @@ class getid3_id3v2 extends getid3_handler
return $clean_genres;
}
/**
* @param array $parsedFrame
*
* @return bool
*/
public function ParseID3v2Frame(&$parsedFrame) {
// shortcuts
@ -657,16 +672,14 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
$parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description']));
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
if (!empty($parsedFrame['framenameshort']) && !empty($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']])) {
@ -678,7 +691,7 @@ class getid3_id3v2 extends getid3_handler
//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
} elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
} elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame
// There may only be one text information frame of its kind in an tag.
// <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
// excluding 'TXXX' described in 4.2.6.>
@ -692,10 +705,10 @@ class getid3_id3v2 extends getid3_handler
}
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
// ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
// This of course breaks when an artist name contains slash character, e.g. "AC/DC"
@ -751,47 +764,29 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
if ($frame_terminatorpos) {
// there are null bytes after the data - this is not according to spec
// only use data up to first null byte
$frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
} else {
// no null bytes following data, just use all data
$frame_urldata = (string) $parsedFrame['data'];
}
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); // according to the frame text encoding
$parsedFrame['url'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1
$parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['url'] = $frame_urldata;
$parsedFrame['description'] = $frame_description;
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
}
unset($parsedFrame['data']);
} elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
} elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames
// There may only be one URL link frame of its kind in a tag,
// except when stated otherwise in the frame description
// <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
// described in 4.3.2.>
// URL <text string>
$parsedFrame['url'] = trim($parsedFrame['data']);
$parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
}
unset($parsedFrame['data']);
@ -813,7 +808,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
$parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
// http://www.getid3.org/phpBB3/viewtopic.php?t=1369
// https://www.getid3.org/phpBB3/viewtopic.php?t=1369
// "this tag typically contains null terminated strings, which are associated in pairs"
// "there are users that use the tag incorrectly"
$IPLS_parts = array();
@ -933,6 +928,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
$parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
$parsedFrame['data'] = substr($parsedFrame['data'], 10);
$deviationbitstream = '';
while ($frame_offset < strlen($parsedFrame['data'])) {
$deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
}
@ -994,19 +990,16 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['language'] = $frame_language;
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
$parsedFrame['description'] = $frame_description;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
@ -1061,7 +1054,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) {
// timestamp probably omitted for first data item
} else {
$parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
@ -1102,19 +1095,16 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['language'] = $frame_language;
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
$parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = $frame_text;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
@ -1407,30 +1397,26 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
if ($id3v2_majorversion == 2) {
$parsedFrame['imagetype'] = $frame_imagetype;
$parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null;
} else {
$parsedFrame['mime'] = $frame_mimetype;
$parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null;
}
$parsedFrame['picturetypeid'] = $frame_picturetype;
$parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
$parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['datalength'] = strlen($parsedFrame['data']);
$parsedFrame['picturetypeid'] = $frame_picturetype;
$parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['datalength'] = strlen($parsedFrame['data']);
$parsedFrame['image_mime'] = '';
$parsedFrame['image_mime'] = '';
$imageinfo = array();
if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
$parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
$parsedFrame['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
if ($imagechunkcheck[0]) {
$parsedFrame['image_width'] = $imagechunkcheck[0];
}
@ -1446,6 +1432,7 @@ class getid3_id3v2 extends getid3_handler
unset($parsedFrame['data']);
break;
}
$dir = '';
if ($this->getid3->option_save_attachments === true) {
// great
/*
@ -1533,11 +1520,8 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
@ -1546,7 +1530,6 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['mime'] = $frame_mimetype;
$parsedFrame['filename'] = $frame_filename;
$parsedFrame['description'] = $frame_description;
unset($parsedFrame['data']);
@ -1616,16 +1599,12 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = $frame_terminatorpos + strlen("\x00");
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen("\x00");
$parsedFrame['ownerid'] = $frame_ownerid;
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['description'] = $frame_description;
unset($parsedFrame['data']);
@ -1721,7 +1700,8 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
@ -1759,6 +1739,7 @@ class getid3_id3v2 extends getid3_handler
$frame_offset += 8;
$parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding));
unset($parsedFrame['data']);
@ -1817,11 +1798,8 @@ class getid3_id3v2 extends getid3_handler
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if description only contains a BOM or terminator then make it blank
$frame_description = '';
}
$parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
$parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
@ -1838,7 +1816,6 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['receivedasid'] = $frame_receivedasid;
$parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
$parsedFrame['sellername'] = $frame_sellername;
$parsedFrame['description'] = $frame_description;
$parsedFrame['mime'] = $frame_mimetype;
$parsedFrame['logo'] = $frame_sellerlogo;
unset($parsedFrame['data']);
@ -2002,9 +1979,9 @@ class getid3_id3v2 extends getid3_handler
// Element ID <text string> $00
// Start time $xx xx xx xx
// End time $xx xx xx xx
// Start offset $xx xx xx xx
// End offset $xx xx xx xx
// <Optional embedded sub-frames>
// Start offset $xx xx xx xx
// End offset $xx xx xx xx
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
@ -2045,7 +2022,7 @@ class getid3_id3v2 extends getid3_handler
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
@ -2065,22 +2042,51 @@ class getid3_id3v2 extends getid3_handler
break;
}
if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
if ($subframe['name'] == 'TIT2') {
switch ($subframe['name']) {
case 'TIT2':
$parsedFrame['chapter_name'] = $encoding_converted_text;
} elseif ($subframe['name'] == 'TIT3') {
$parsedFrame['subframes'][] = $subframe;
break;
case 'TIT3':
$parsedFrame['chapter_description'] = $encoding_converted_text;
}
$parsedFrame['subframes'][] = $subframe;
} else {
$this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
$parsedFrame['subframes'][] = $subframe;
break;
case 'WXXX':
list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2);
$parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url'];
$parsedFrame['subframes'][] = $subframe;
break;
case 'APIC':
if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) {
list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches;
$subframe['image_mime'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime));
$subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype);
$subframe['description'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description));
if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) {
// the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16)
// the above regex assumes one byte, if it's actually two then strip the second one here
$subframe_apic_picturedata = substr($subframe_apic_picturedata, 1);
}
$subframe['data'] = $subframe_apic_picturedata;
unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata);
unset($subframe['text'], $parsedFrame['text']);
$parsedFrame['subframes'][] = $subframe;
$parsedFrame['picture_present'] = true;
} else {
$this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format');
}
break;
default:
$this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)');
break;
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC
}
$id3v2_chapter_entry = array();
foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) {
if (isset($parsedFrame[$id3v2_chapter_key])) {
$id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
}
@ -2099,7 +2105,7 @@ class getid3_id3v2 extends getid3_handler
// CTOC flags %xx
// Entry count $xx
// Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
// <Optional embedded sub-frames>
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
@ -2181,11 +2187,20 @@ class getid3_id3v2 extends getid3_handler
return true;
}
/**
* @param string $data
*
* @return string
*/
public function DeUnsynchronise($data) {
return str_replace("\xFF\x00", "\xFF", $data);
}
/**
* @param int $index
*
* @return string
*/
public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
0x00 => 'No more than 128 frames and 1 MB total tag size',
@ -2196,6 +2211,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
static $LookupExtendedHeaderRestrictionsTextEncodings = array(
0x00 => 'No restrictions',
@ -2204,6 +2224,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
0x00 => 'No restrictions',
@ -2214,6 +2239,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
static $LookupExtendedHeaderRestrictionsImageEncoding = array(
0x00 => 'No restrictions',
@ -2222,6 +2252,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
0x00 => 'No restrictions',
@ -2232,6 +2267,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
}
/**
* @param string $currencyid
*
* @return string
*/
public function LookupCurrencyUnits($currencyid) {
$begin = __LINE__;
@ -2428,7 +2468,11 @@ class getid3_id3v2 extends getid3_handler
return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
}
/**
* @param string $currencyid
*
* @return string
*/
public function LookupCurrencyCountry($currencyid) {
$begin = __LINE__;
@ -2624,8 +2668,12 @@ class getid3_id3v2 extends getid3_handler
return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
}
/**
* @param string $languagecode
* @param bool $casesensitive
*
* @return string
*/
public static function LanguageLookup($languagecode, $casesensitive=false) {
if (!$casesensitive) {
@ -3081,7 +3129,11 @@ class getid3_id3v2 extends getid3_handler
return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
}
/**
* @param int $index
*
* @return string
*/
public static function ETCOEventLookup($index) {
if (($index >= 0x17) && ($index <= 0xDF)) {
return 'reserved for future use';
@ -3125,6 +3177,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public static function SYTLContentTypeLookup($index) {
static $SYTLContentTypeLookup = array(
0x00 => 'other',
@ -3141,6 +3198,12 @@ class getid3_id3v2 extends getid3_handler
return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
}
/**
* @param int $index
* @param bool $returnarray
*
* @return array|string
*/
public static function APICPictureTypeLookup($index, $returnarray=false) {
static $APICPictureTypeLookup = array(
0x00 => 'Other',
@ -3171,6 +3234,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public static function COMRReceivedAsLookup($index) {
static $COMRReceivedAsLookup = array(
0x00 => 'Other',
@ -3187,6 +3255,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
}
/**
* @param int $index
*
* @return string
*/
public static function RVA2ChannelTypeLookup($index) {
static $RVA2ChannelTypeLookup = array(
0x00 => 'Other',
@ -3203,6 +3276,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
}
/**
* @param string $framename
*
* @return string
*/
public static function FrameNameLongLookup($framename) {
$begin = __LINE__;
@ -3354,7 +3432,7 @@ class getid3_id3v2 extends getid3_handler
TYER Year
UFI Unique file identifier
UFID Unique file identifier
ULT Unsychronised lyric/text transcription
ULT Unsynchronised lyric/text transcription
USER Terms of use
USLT Unsynchronised lyric/text transcription
WAF Official audio file webpage
@ -3386,7 +3464,11 @@ class getid3_id3v2 extends getid3_handler
// from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
}
/**
* @param string $framename
*
* @return string
*/
public static function FrameNameShortLookup($framename) {
$begin = __LINE__;
@ -3538,7 +3620,7 @@ class getid3_id3v2 extends getid3_handler
TYER year
UFI unique_file_identifier
UFID unique_file_identifier
ULT unsychronised_lyric
ULT unsynchronised_lyric
USER terms_of_use
USLT unsynchronised_lyric
WAF url_file
@ -3566,6 +3648,11 @@ class getid3_id3v2 extends getid3_handler
return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
}
/**
* @param string $encoding
*
* @return string
*/
public static function TextEncodingTerminatorLookup($encoding) {
// http://www.id3.org/id3v2.4.0-structure.txt
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
@ -3579,6 +3666,11 @@ class getid3_id3v2 extends getid3_handler
return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
}
/**
* @param int $encoding
*
* @return string
*/
public static function TextEncodingNameLookup($encoding) {
// http://www.id3.org/id3v2.4.0-structure.txt
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
@ -3592,6 +3684,41 @@ class getid3_id3v2 extends getid3_handler
return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
}
/**
* @param string $string
* @param string $terminator
*
* @return string
*/
public static function RemoveStringTerminator($string, $terminator) {
// Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present.
// https://github.com/JamesHeinrich/getID3/issues/121
// https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227
if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) {
$string = substr($string, 0, -strlen($terminator));
}
return $string;
}
/**
* @param string $string
*
* @return string
*/
public static function MakeUTF16emptyStringEmpty($string) {
if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
// if string only contains a BOM or terminator then make it actually an empty string
$string = '';
}
return $string;
}
/**
* @param string $framename
* @param int $id3v2majorversion
*
* @return bool|int
*/
public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
switch ($id3v2majorversion) {
case 2:
@ -3606,12 +3733,19 @@ class getid3_id3v2 extends getid3_handler
return false;
}
/**
* @param string $numberstring
* @param bool $allowdecimal
* @param bool $allownegative
*
* @return bool
*/
public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
for ($i = 0; $i < strlen($numberstring); $i++) {
if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
if (($numberstring{$i} == '.') && $allowdecimal) {
if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) {
if (($numberstring[$i] == '.') && $allowdecimal) {
// allowed
} elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
} elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) {
// allowed
} else {
return false;
@ -3621,6 +3755,11 @@ class getid3_id3v2 extends getid3_handler
return true;
}
/**
* @param string $datestamp
*
* @return bool
*/
public static function IsValidDateStampString($datestamp) {
if (strlen($datestamp) != 8) {
return false;
@ -3649,10 +3788,20 @@ class getid3_id3v2 extends getid3_handler
return true;
}
/**
* @param int $majorversion
*
* @return int
*/
public static function ID3v2HeaderLength($majorversion) {
return (($majorversion == 2) ? 6 : 10);
}
/**
* @param string $frame_name
*
* @return string|false
*/
public static function ID3v22iTunesBrokenFrameName($frame_name) {
// iTunes (multiple versions) has been known to write ID3v2.3 style frames
// but use ID3v2.2 frame names, right-padded using either [space] or [null]
@ -3739,3 +3888,4 @@ class getid3_id3v2 extends getid3_handler
}
}

View File

@ -1,11 +1,11 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
/// //
// module.tag.lyrics3.php //
@ -17,7 +17,9 @@
class getid3_lyrics3 extends getid3_handler
{
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
@ -61,7 +63,7 @@ class getid3_lyrics3 extends getid3_handler
// Lyrics3v2, no ID3v1, no APE
$lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
$lyrics3size = (int) strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
$lyrics3offset = $info['filesize'] - $lyrics3size;
$lyrics3version = 2;
@ -96,7 +98,7 @@ class getid3_lyrics3 extends getid3_handler
}
if (isset($lyrics3offset)) {
if (isset($lyrics3offset) && isset($lyrics3version) && isset($lyrics3size)) {
$info['avdataend'] = $lyrics3offset;
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
@ -126,6 +128,13 @@ class getid3_lyrics3 extends getid3_handler
return true;
}
/**
* @param int $endoffset
* @param int $version
* @param int $length
*
* @return bool
*/
public function getLyrics3Data($endoffset, $version, $length) {
// http://www.volweb.cz/str/tags.htm
@ -142,6 +151,8 @@ class getid3_lyrics3 extends getid3_handler
}
$rawdata = $this->fread($length);
$ParsedLyrics3 = array();
$ParsedLyrics3['raw']['lyrics3version'] = $version;
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
$ParsedLyrics3['tag_offset_start'] = $endoffset;
@ -250,6 +261,11 @@ class getid3_lyrics3 extends getid3_handler
return true;
}
/**
* @param string $rawtimestamp
*
* @return int|false
*/
public function Lyrics3Timestamp2Seconds($rawtimestamp) {
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
return (int) (($regs[1] * 60) + $regs[2]);
@ -257,8 +273,14 @@ class getid3_lyrics3 extends getid3_handler
return false;
}
/**
* @param array $Lyrics3data
*
* @return bool
*/
public function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
$notimestamplyricsarray = array();
foreach ($lyricsarray as $key => $lyricline) {
$regs = array();
unset($thislinetimestamps);
@ -287,6 +309,11 @@ class getid3_lyrics3 extends getid3_handler
return true;
}
/**
* @param string $char
*
* @return bool|null
*/
public function IntString2Bool($char) {
if ($char == '1') {
return true;

View File

@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// or https://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
@ -18,9 +18,9 @@ GNU GPL: https://gnu.org/licenses/gpl.html (v3)
GNU LGPL: https://gnu.org/licenses/lgpl.html (v3)
Mozilla MPL: http://www.mozilla.org/MPL/2.0/ (v2)
Mozilla MPL: https://www.mozilla.org/MPL/2.0/ (v2)
getID3 Commercial License: http://getid3.org/#gCL (payment required)
getID3 Commercial License: https://www.getid3.org/#gCL (payment required)
*****************************************************************
*****************************************************************
@ -28,10 +28,10 @@ Copies of each of the above licenses are included in the 'licenses'
directory of the getID3 distribution.
+---------------------------------------------+
| If you want to donate, there is a link on |
| http://www.getid3.org for PayPal donations. |
+---------------------------------------------+
+----------------------------------------------+
| If you want to donate, there is a link on |
| https://www.getid3.org for PayPal donations. |
+----------------------------------------------+
Quick Start
@ -77,7 +77,7 @@ Reads & parses (to varying degrees):
¤ audio-lossy:
* MP3/MP2/MP1
* MPC / Musepack
* Ogg (Vorbis, OggFLAC, Speex)
* Ogg (Vorbis, OggFLAC, Speex, Opus)
* AAC / MP4
* AC3
* DTS
@ -145,9 +145,10 @@ Writes:
Requirements
===========================================================================
* PHP 4.2.0 up to 5.2.x for getID3() 1.7.x (and earlier)
* PHP 5.0.5 (or higher) for getID3() 1.8.x (and up)
* PHP 5.0.5 (or higher) for getID3() 2.0.x (and up)
* PHP 4.2.0 up to 5.2.x for getID3() 1.7.x (and earlier)
* PHP 5.0.5 (or higher) for getID3() 1.8.x (and up)
* PHP 5.3.0 (or higher) for getID3() 1.9.17 (and up)
* PHP 5.3.0 (or higher) for getID3() 2.0.x (and up)
* at least 4MB memory for PHP. 8MB or more is highly recommended.
12MB is required with all modules loaded.
@ -177,24 +178,32 @@ like this:
// Copy remote file locally to scan with getID3()
$remotefilename = 'http://www.example.com/filename.mp3';
if ($fp_remote = fopen($remotefilename, 'rb')) {
$localtempfilename = tempnam('/tmp', 'getID3');
if ($fp_local = fopen($localtempfilename, 'wb')) {
while ($buffer = fread($fp_remote, 8192)) {
fwrite($fp_local, $buffer);
}
fclose($fp_local);
$localtempfilename = tempnam('/tmp', 'getID3');
if ($fp_local = fopen($localtempfilename, 'wb')) {
while ($buffer = fread($fp_remote, 32768)) {
fwrite($fp_local, $buffer);
}
fclose($fp_local);
$remote_headers = array_change_key_case(get_headers($remotefilename, 1), CASE_LOWER);
$remote_filesize = (isset($remote_headers['content-length']) ? (is_array($remote_headers['content-length']) ? $remote_headers['content-length'][count($remote_headers['content-length']) - 1] : $remote_headers['content-length']) : null);
// Initialize getID3 engine
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($filename);
$ThisFileInfo = $getID3->analyze($localtempfilename, $remote_filesize, basename($remotefilename));
// Delete temporary file
unlink($localtempfilename);
}
fclose($fp_remote);
// Delete temporary file
unlink($localtempfilename);
}
fclose($fp_remote);
}
Note: since v1.9.9-20150212 it is possible a second and third parameter
to $getID3->analyze(), for original filesize and original filename
respectively. This permits you to download only a portion of a large remote
file but get accurate playtime estimates, assuming the format only requires
the beginning of the file for correct format analysis.
See /demos/demo.write.php for how to write tags.
@ -292,7 +301,7 @@ could essentially write it today with a one-line function:
Future Plans
===========================================================================
http://www.getid3.org/phpBB3/viewforum.php?f=7
https://www.getid3.org/phpBB3/viewforum.php?f=7
* Better support for MP4 container format
* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
@ -334,11 +343,11 @@ http://www.getid3.org/phpBB3/viewforum.php?f=7
* Optional scan-through-frames for AVI verification
(thanks rockcohenØmassive-interactive*nl)
* Support for TTF (thanks infoØbutterflyx*com)
* Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171)
* Support for DSS (https://www.getid3.org/phpBB3/viewtopic.php?t=171)
* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
http://www.getid3.org/phpBB3/viewtopic.php?t=182
* Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
https://www.getid3.org/phpBB3/viewtopic.php?t=182
* Support for AMR (https://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for 3gpp (https://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
* Parse XML data returned in Ogg comments
* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
@ -374,7 +383,7 @@ http://www.getid3.org/phpBB3/viewforum.php?f=7
Known Bugs/Issues in getID3() that may be fixed eventually
===========================================================================
http://www.getid3.org/phpBB3/viewtopic.php?t=25
https://www.getid3.org/phpBB3/viewtopic.php?t=25
* Cannot determine bitrate for MPEG video with VBR video data
(need documentation)
@ -400,7 +409,7 @@ http://www.getid3.org/phpBB3/viewtopic.php?t=25
Known Bugs/Issues in getID3() that cannot be fixed
--------------------------------------------------
http://www.getid3.org/phpBB3/viewtopic.php?t=25
https://www.getid3.org/phpBB3/viewtopic.php?t=25
* 32-bit PHP installations only:
Files larger than 2GB cannot always be parsed fully by getID3()
@ -430,8 +439,15 @@ http://www.getid3.org/phpBB3/viewtopic.php?t=25
Known Bugs/Issues in other programs
-----------------------------------
http://www.getid3.org/phpBB3/viewtopic.php?t=25
https://www.getid3.org/phpBB3/viewtopic.php?t=25
* MusicBrainz Picard (at least up to v1.3.2) writes multiple
ID3v2.3 genres in non-standard forward-slash separated text
rather than parenthesis-numeric+refinement style per the ID3v2.3
specs. Tags written in ID3v2.4 mode are written correctly.
(detected and worked around by getID3())
* PZ TagEditor v4.53.408 has been known to insert ID3v2.3 frames
into an existing ID3v2.2 tag which, of course, breaks things
* Windows Media Player (up to v11) and iTunes (up to v10+) do
not correctly handle ID3v2.3 tags with UTF-16BE+BOM
encoding (they assume the data is UTF-16LE+BOM and either
@ -451,6 +467,11 @@ http://www.getid3.org/phpBB3/viewtopic.php?t=25
written just "value" (detected by getID3())
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
actually ABR or VBR.
* iTunes (versions "v7.0.0.70" is known-guilty, probably
other versions are too) writes ID3v2.3 comment tags using an
ID3v2.2 frame name (3-bytes) null-padded to 4 bytes which is
not valid for ID3v2.3+
(detected by getID3() since 1.9.12-201603221746)
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
other versions are too) writes ID3v2.3 comment tags using a
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
@ -601,4 +622,6 @@ Reference material:
* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
* http://trac.musepack.net/trac/wiki/SV8Specification
* http://wyday.com/cuesharp/specification.php
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
* http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf

View File

@ -13,7 +13,7 @@
*
* @global string $wp_version
*/
$wp_version = '5.3-alpha-46111';
$wp_version = '5.3-alpha-46112';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.