summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Arthur <paul.arthur@flowerysong.com>2013-05-13 16:51:34 -0400
committerPaul Arthur <paul.arthur@flowerysong.com>2013-05-13 16:51:34 -0400
commit6bed09fbc9e5b18a925e48c7fc41f9ca29055c47 (patch)
treef368bdf30f46e486bc75b044030e9b2c2453098e
parent1d881ae0180766226229a8008f8b59362b5eaa23 (diff)
downloadampache-6bed09fbc9e5b18a925e48c7fc41f9ca29055c47.tar.gz
ampache-6bed09fbc9e5b18a925e48c7fc41f9ca29055c47.tar.bz2
ampache-6bed09fbc9e5b18a925e48c7fc41f9ca29055c47.zip
Bump getID3 version
-rwxr-xr-xdocs/CHANGELOG.md1
-rw-r--r--modules/getid3/docs/changelog.txt30
-rw-r--r--modules/getid3/docs/readme.txt3
-rw-r--r--modules/getid3/getid3.lib.php30
-rw-r--r--modules/getid3/getid3.php82
-rw-r--r--modules/getid3/module.archive.gzip.php3
-rw-r--r--modules/getid3/module.archive.szip.php16
-rw-r--r--modules/getid3/module.archive.zip.php149
-rw-r--r--modules/getid3/module.audio-video.matroska.php50
-rw-r--r--modules/getid3/module.audio-video.riff.php268
-rw-r--r--modules/getid3/module.audio.ac3.php35
-rw-r--r--modules/getid3/module.audio.dss.php37
-rw-r--r--modules/getid3/module.audio.dts.php140
-rw-r--r--modules/getid3/module.audio.flac.php55
-rw-r--r--modules/getid3/module.graphic.jpg.php3
15 files changed, 550 insertions, 352 deletions
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 40236cd8..f4dd5eb4 100755
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -3,6 +3,7 @@ CHANGELOG
3.6-FUTURE
----------
+- Updated getID3 to 1.9.5
- Improved the performance of stream playlist creation (reported by AkbarSerad)
- Fixed "Pure Random" / Random URLs (reported by mafe)
diff --git a/modules/getid3/docs/changelog.txt b/modules/getid3/docs/changelog.txt
index bbaedf10..ee8cd4f7 100644
--- a/modules/getid3/docs/changelog.txt
+++ b/modules/getid3/docs/changelog.txt
@@ -17,6 +17,36 @@
Version History
===============
+1.9.5: [2013-02-20] James Heinrich, Dmitry Arkhipov
+ » DTS-in-WAV now properly supported
+ € DSS files return additional data in new keys, and some existing
+ keys have been renamed
+ * Bugfix: open_basedir not parsed correctly under Windows
+ (thanks yannick*jamontØgmail*com)
+ * Bugfix: [demo/demo.browse] might not display file or directory name
+ on PHP >=5.4.0 if filename not UTF-8 friendly
+ * Bugfix: [demo/demo.zip] could read more uncompressed data than
+ required; fail to read file if local data descriptor not set;
+ some wrong include files were listed; improved error message display
+ * Bugfix: [module.audio-video.riff] INFO comment chunks with null name
+ chunk not parsed correctly
+ * Bugfix: [module.archive.gz] gzip files with filename stored may have
+ filename reduplicated in [gzip][files] output
+ * Bugfix: [module.archive.zip] data_descriptor not parsed correctly
+ * Bugfix: [module.archive.zip] some newer compression methods unknown
+ * Bugfix: [module.archive.zip] not all flags parsed
+ * Bugfix: [module.archive.zip] local file header not parsed correctly
+ if file has zero values for compressed_size in Local File Header
+ * Bugfix: (#1493) better support for >2GB filesize on 32-bit Linux
+ * Bugfix: (#1474) unneccesary call to GetDataImageSize in JPEG module
+ * Bugfix: (#1470) GIF files falsely detected as TS format
+ * Bugfix: (#1431) Matroska did not parse PixelCrop* / DisplayUnit
+ (thanks jgerberØwikimedia*org)
+ * Bugfix: (#1430) split ID3v2 text values on null separator
+ * Bugfix: (#1426) MS Office 2007 file format now recognized as zip.msoffice
+ * Bugfix: (#1423) optimized CreateDeepArray function
+ * Bugfix: (#1415) add support for DS2 variant of DSS
+
1.9.4b1: [2012-10-05] James Heinrich, Dmitry Arkhipov, Karl G. Holz
» New module: extension.cache.sqlite3.php (by Karl G. Holz)
» New demo: demos/getid3.demo.dirscan.php (by Karl G. Holz)
diff --git a/modules/getid3/docs/readme.txt b/modules/getid3/docs/readme.txt
index de960965..548ba643 100644
--- a/modules/getid3/docs/readme.txt
+++ b/modules/getid3/docs/readme.txt
@@ -66,6 +66,7 @@ Reads & parses (to varying degrees):
* MP3/MP2/MP1
* MPC / Musepack
* Ogg (Vorbis, OggFLAC, Speex)
+ * AAC / MP4
* AC3
* DTS
* RealAudio
@@ -99,7 +100,7 @@ Reads & parses (to varying degrees):
* Matroska (MKV)
* MPEG-1 / MPEG-2
* NSV (Nullsoft Streaming Video)
- * Quicktime
+ * Quicktime (including MP4)
* RealVideo
€ still image:
diff --git a/modules/getid3/getid3.lib.php b/modules/getid3/getid3.lib.php
index c5bfda04..08c3067d 100644
--- a/modules/getid3/getid3.lib.php
+++ b/modules/getid3/getid3.lib.php
@@ -481,9 +481,7 @@ class getid3_lib
// $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
// or
// $foo['path']['to']['my'] = 'file.txt';
- while ($ArrayPath && ($ArrayPath{0} == $Separator)) {
- $ArrayPath = substr($ArrayPath, 1);
- }
+ $ArrayPath = ltrim($ArrayPath, $Separator);
if (($pos = strpos($ArrayPath, $Separator)) !== false) {
$ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
} else {
@@ -1150,7 +1148,7 @@ class getid3_lib
if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
fwrite($tmp, $imgData);
fclose($tmp);
- $GetDataImageSize = @GetImageSize($tempfilename, $imageinfo);
+ $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
}
unlink($tempfilename);
}
@@ -1317,4 +1315,28 @@ class getid3_lib
return trim($string, "\x00");
}
+ public static function getFileSizeSyscall($path) {
+ $filesize = false;
+
+ if (GETID3_OS_ISWINDOWS) {
+ if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
+ $filesystem = new COM('Scripting.FileSystemObject');
+ $file = $filesystem->GetFile($path);
+ $filesize = $file->Size();
+ unset($filesystem, $file);
+ } else {
+ $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
+ }
+ } else {
+ $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
+ }
+ if (isset($commandline)) {
+ $output = trim(`$commandline`);
+ if (ctype_digit($output)) {
+ $filesize = (float) $output;
+ }
+ }
+ return $filesize;
+ }
+
}
diff --git a/modules/getid3/getid3.php b/modules/getid3/getid3.php
index 98bfb0a0..55eb8f03 100644
--- a/modules/getid3/getid3.php
+++ b/modules/getid3/getid3.php
@@ -9,6 +9,15 @@
// ///
/////////////////////////////////////////////////////////////////
+// define a constant rather than looking up every time it is needed
+if (!defined('GETID3_OS_ISWINDOWS')) {
+ define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
+}
+// Get base path of getID3() - ONCE
+if (!defined('GETID3_INCLUDEPATH')) {
+ define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
+}
+
// 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))) {
@@ -29,7 +38,7 @@ if ($open_basedir) {
$temp_dir .= DIRECTORY_SEPARATOR;
}
$found_valid_tempdir = false;
- $open_basedirs = explode(':', $open_basedir);
+ $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
foreach ($open_basedirs as $basedir) {
if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
$basedir .= DIRECTORY_SEPARATOR;
@@ -51,17 +60,6 @@ if (!$temp_dir) {
define('GETID3_TEMP_DIR', $temp_dir);
unset($open_basedir, $temp_dir);
-
-// define a constant rather than looking up every time it is needed
-if (!defined('GETID3_OS_ISWINDOWS')) {
- define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
-}
-
-// Get base path of getID3() - ONCE
-if (!defined('GETID3_INCLUDEPATH')) {
- define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
-}
-
// End: Defines
@@ -105,7 +103,7 @@ class getID3
protected $startup_warning = '';
protected $memory_limit = 0;
- const VERSION = '1.9.4b1-20121005';
+ const VERSION = '1.9.5-20130220';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
@@ -285,30 +283,8 @@ class getID3
if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
($this->info['filesize'] < 0) ||
(ftell($this->fp) < 0)) {
- $real_filesize = false;
- if (GETID3_OS_ISWINDOWS) {
- if (class_exists('COM')) {
- $filesystem = new COM('Scripting.FileSystemObject');
- $file = $filesystem->GetFile($this->info['filenamepath']);
- $real_filesize = $file->Size();
- unset($filesystem, $file);
- } else {
- // From PHP 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
- $this->warning('COM class not available.'.(version_compare(PHP_VERSION, '5.4.5', '>=') ? ' COM and DOTNET support are no longer built into PHP since v5.4.5, please enable in php.ini' : ''));
+ $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
- $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
- $dir_output = `$commandline`;
- if (preg_match('#1 File\(s\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
- $real_filesize = (float) $matches[1];
- }
- }
- } else {
- $commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($this->info['filenamepath']);
- $dir_output = `$commandline`;
- if (preg_match('#([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.str_replace('#', '\\#', preg_quote($filename)).'$#', $dir_output, $matches)) {
- $real_filesize = (float) $matches[1];
- }
- }
if ($real_filesize === false) {
unset($this->info['filesize']);
fclose($this->fp);
@@ -449,9 +425,6 @@ class getID3
return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
}
$class = new $class_name($this);
- //if (!empty($determined_format['set_inline_attachments'])) {
- // $class->inline_attachments = $this->option_save_attachments;
- //}
$class->Analyze();
unset($class);
@@ -497,7 +470,7 @@ class getID3
}
- // public: error handling
+ // private: error handling
public function error($message) {
$this->CleanUp();
if (!isset($this->info['error'])) {
@@ -508,7 +481,7 @@ class getID3
}
- // public: warning handling
+ // private: warning handling
public function warning($message) {
$this->info['warning'][] = $message;
return true;
@@ -633,7 +606,7 @@ class getID3
// DSS - audio - Digital Speech Standard
'dss' => array(
- 'pattern' => '^[\x02-\x03]dss',
+ 'pattern' => '^[\x02-\x03]ds[s2]',
'group' => 'audio',
'module' => 'dss',
'mime_type' => 'application/octet-stream',
@@ -653,7 +626,6 @@ class getID3
'group' => 'audio',
'module' => 'flac',
'mime_type' => 'audio/x-flac',
- //'set_inline_attachments' => true,
),
// LA - audio - Lossless Audio (LA)
@@ -833,7 +805,6 @@ class getID3
'group' => 'audio-video',
'module' => 'matroska',
'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
- //'set_inline_attachments' => true,
),
// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
@@ -860,7 +831,6 @@ class getID3
'mime_type' => 'application/ogg',
'fail_id3' => 'WARNING',
'fail_ape' => 'WARNING',
- //'set_inline_attachments' => true,
),
// QT - audio/video - Quicktime
@@ -898,7 +868,7 @@ class getID3
// TS - audio/video - MPEG-2 Transport Stream
'ts' => array(
- 'pattern' => '^\x47',
+ 'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
'group' => 'audio-video',
'module' => 'ts',
'mime_type' => 'video/MP2T',
@@ -1528,7 +1498,7 @@ class getID3
public function CalculateCompressionRatioAudio() {
- if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
+ 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;
}
$this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
@@ -1671,7 +1641,7 @@ abstract class getid3_handler
return fread($this->getid3->fp, $bytes);
}
- protected function fseek($bytes, $whence = SEEK_SET) {
+ protected function fseek($bytes, $whence=SEEK_SET) {
if ($this->data_string_flag) {
switch ($whence) {
case SEEK_SET:
@@ -1766,7 +1736,7 @@ abstract class getid3_handler
$buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
$bytesleft = $length;
while ($bytesleft > 0) {
- if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false) {
+ if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
}
$bytesleft -= $byteswritten;
@@ -1777,17 +1747,23 @@ abstract class getid3_handler
}
- } catch(Exception $e) {
+ } catch (Exception $e) {
- if (isset($fp_dest) && is_resource($fp_dest)) { // close and remove dest file if created
+ // close and remove dest file if created
+ if (isset($fp_dest) && is_resource($fp_dest)) {
fclose($fp_dest);
unlink($dest);
}
- $attachment = null; // do not set any is case of error
+
+ // do not set any is case of error
+ $attachment = null;
$this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
- return false;
}
+
+ // seek to the end of attachment
+ $this->fseek($offset + $length);
+
return $attachment;
}
diff --git a/modules/getid3/module.archive.gzip.php b/modules/getid3/module.archive.gzip.php
index f8818df5..182351f9 100644
--- a/modules/getid3/module.archive.gzip.php
+++ b/modules/getid3/module.archive.gzip.php
@@ -140,8 +140,9 @@ class getid3_gzip extends getid3_handler {
//|...original file name, zero-terminated...|
//+=========================================+
// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
- $thisInfo['filename'] = preg_replace('#\.gz$#i', '', $info['filename']);
+ $thisInfo['filename'] = preg_replace('#\\.gz$#i', '', $info['filename']);
if ($thisInfo['flags']['filename']) {
+ $thisInfo['filename'] = '';
while (true) {
if (ord($buff[$fpointer]) == 0) {
$fpointer++;
diff --git a/modules/getid3/module.archive.szip.php b/modules/getid3/module.archive.szip.php
index 8741e722..6f41d2f3 100644
--- a/modules/getid3/module.archive.szip.php
+++ b/modules/getid3/module.archive.szip.php
@@ -20,8 +20,8 @@ class getid3_szip extends getid3_handler
public function Analyze() {
$info = &$this->getid3->info;
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
- $SZIPHeader = fread($this->getid3->fp, 6);
+ $this->fseek($info['avdataoffset']);
+ $SZIPHeader = $this->fread(6);
if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") {
$info['error'][] = 'Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"';
return false;
@@ -29,20 +29,22 @@ class getid3_szip extends getid3_handler
$info['fileformat'] = 'szip';
$info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
$info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
+$info['error'][] = 'SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
+return false;
- while (!feof($this->getid3->fp)) {
- $NextBlockID = fread($this->getid3->fp, 2);
+ while (!$this->feof()) {
+ $NextBlockID = $this->fread(2);
switch ($NextBlockID) {
case 'SZ':
// Note that szip files can be concatenated, this has the same effect as
// concatenating the files. this also means that global header blocks
// might be present between directory/data blocks.
- fseek($this->getid3->fp, 4, SEEK_CUR);
+ $this->fseek(4, SEEK_CUR);
break;
case 'BH':
- $BHheaderbytes = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 3));
- $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes);
+ $BHheaderbytes = getid3_lib::BigEndian2Int($this->fread(3));
+ $BHheaderdata = $this->fread($BHheaderbytes);
$BHheaderoffset = 0;
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
//filename as \0 terminated string (empty string indicates end)
diff --git a/modules/getid3/module.archive.zip.php b/modules/getid3/module.archive.zip.php
index e846a20a..7756c56d 100644
--- a/modules/getid3/module.archive.zip.php
+++ b/modules/getid3/module.archive.zip.php
@@ -53,7 +53,8 @@ class getid3_zip extends getid3_handler
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
- if ($centraldirectoryentry['uncompressed_size'] > 0) {
+ //if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid
+ if (!empty($centraldirectoryentry['filename'])) {
$info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
}
}
@@ -77,33 +78,54 @@ class getid3_zip extends getid3_handler
$info['zip']['compression_speed'] = 'store';
}
- return true;
+ // secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each
+ // Local File Header entry will
+ foreach ($info['zip']['central_directory'] as $central_directory_entry) {
+ fseek($this->getid3->fp, $central_directory_entry['entry_offset'], SEEK_SET);
+ if ($fileentry = $this->ZIPparseLocalFileHeader()) {
+ $info['zip']['entries'][] = $fileentry;
+ } else {
+ $info['warning'][] = 'Error parsing Local File Header at offset '.$central_directory_entry['entry_offset'];
+ }
+ }
+
+ if (!empty($info['zip']['files']['[Content_Types].xml']) &&
+ !empty($info['zip']['files']['_rels']['.rels']) &&
+ !empty($info['zip']['files']['docProps']['app.xml']) &&
+ !empty($info['zip']['files']['docProps']['core.xml'])) {
+ // http://technet.microsoft.com/en-us/library/cc179224.aspx
+ $info['fileformat'] = 'zip.msoffice';
+ if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
+ } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+ } elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+ }
+ }
+ return true;
}
}
}
- if ($this->getZIPentriesFilepointer()) {
-
- // central directory couldn't be found and/or parsed
- // scan through actual file data entries, recover as much as possible from probable trucated file
- if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) {
- $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)';
- }
- $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
- foreach ($info['zip']['entries'] as $key => $valuearray) {
- $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
- }
- return true;
-
- } else {
-
+ if (!$this->getZIPentriesFilepointer()) {
unset($info['zip']);
$info['fileformat'] = '';
$info['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
return false;
+ }
+ // central directory couldn't be found and/or parsed
+ // scan through actual file data entries, recover as much as possible from probable trucated file
+ if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) {
+ $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)';
+ }
+ $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
+ foreach ($info['zip']['entries'] as $key => $valuearray) {
+ $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
}
+ return true;
}
@@ -182,7 +204,7 @@ class getid3_zip extends getid3_handler
$ZIPlocalFileHeader = fread($this->getid3->fp, 30);
$LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
- if ($LocalFileHeader['raw']['signature'] != 0x04034B50) {
+ if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // "PK\x03\x04"
// invalid Local File Header Signature
fseek($this->getid3->fp, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
return false;
@@ -218,17 +240,56 @@ class getid3_zip extends getid3_handler
}
}
+ if ($LocalFileHeader['compressed_size'] == 0) {
+ // *Could* be a zero-byte file
+ // But could also be a file written on the fly that didn't know compressed filesize beforehand.
+ // Correct compressed filesize should be in the data_descriptor located after this file data, and also in Central Directory (at end of zip file)
+ if (!empty($this->getid3->info['zip']['central_directory'])) {
+ foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
+ if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
+ if ($central_directory_entry['compressed_size'] > 0) {
+ // overwrite local zero value (but not ['raw']'compressed_size']) so that seeking for data_descriptor (and next file entry) works correctly
+ $LocalFileHeader['compressed_size'] = $central_directory_entry['compressed_size'];
+ }
+ break;
+ }
+ }
+ }
+
+ }
$LocalFileHeader['data_offset'] = ftell($this->getid3->fp);
- //$LocalFileHeader['compressed_data'] = fread($this->getid3->fp, $LocalFileHeader['raw']['compressed_size']);
- fseek($this->getid3->fp, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR);
+ fseek($this->getid3->fp, $LocalFileHeader['compressed_size'], SEEK_CUR); // this should (but may not) match value in $LocalFileHeader['raw']['compressed_size'] -- $LocalFileHeader['compressed_size'] could have been overwritten above with value from Central Directory
if ($LocalFileHeader['flags']['data_descriptor_used']) {
- $DataDescriptor = fread($this->getid3->fp, 12);
- $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
- $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
- $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
- }
+ $DataDescriptor = fread($this->getid3->fp, 16);
+ $LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
+ if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08"
+ $this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.(ftell($this->getid3->fp) - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']);
+ fseek($this->getid3->fp, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
+ return false;
+ }
+ $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
+ $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
+ $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 12, 4));
+ if (!$LocalFileHeader['raw']['compressed_size'] && $LocalFileHeader['data_descriptor']['compressed_size']) {
+ foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
+ if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
+ if ($LocalFileHeader['data_descriptor']['compressed_size'] == $central_directory_entry['compressed_size']) {
+ // $LocalFileHeader['compressed_size'] already set from Central Directory
+ } else {
+ $this->getid3->info['warning'][] = 'conflicting compressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['compressed_size'].') vs Central Directory ('.$central_directory_entry['compressed_size'].') for file at offset '.$LocalFileHeader['offset'];
+ }
+ if ($LocalFileHeader['data_descriptor']['uncompressed_size'] == $central_directory_entry['uncompressed_size']) {
+ $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['data_descriptor']['uncompressed_size'];
+ } else {
+ $this->getid3->info['warning'][] = 'conflicting uncompressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['uncompressed_size'].') vs Central Directory ('.$central_directory_entry['uncompressed_size'].') for file at offset '.$LocalFileHeader['offset'];
+ }
+ break;
+ }
+ }
+ }
+ }
return $LocalFileHeader;
}
@@ -317,7 +378,23 @@ class getid3_zip extends getid3_handler
public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
- $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
+ // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html
+ $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
+ // 0x0002 -- see below
+ // 0x0004 -- see below
+ $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
+ $ParsedFlags['enhanced_deflation'] = (bool) ($flagbytes & 0x0010);
+ $ParsedFlags['compressed_patched_data'] = (bool) ($flagbytes & 0x0020);
+ $ParsedFlags['strong_encryption'] = (bool) ($flagbytes & 0x0040);
+ // 0x0080 - unused
+ // 0x0100 - unused
+ // 0x0200 - unused
+ // 0x0400 - unused
+ $ParsedFlags['language_encoding'] = (bool) ($flagbytes & 0x0800);
+ // 0x1000 - reserved
+ $ParsedFlags['mask_header_values'] = (bool) ($flagbytes & 0x2000);
+ // 0x4000 - reserved
+ // 0x8000 - reserved
switch ($compressionmethod) {
case 6:
@@ -343,7 +420,6 @@ class getid3_zip extends getid3_handler
}
break;
}
- $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
return $ParsedFlags;
}
@@ -368,13 +444,16 @@ class getid3_zip extends getid3_handler
14 => 'VFAT',
15 => 'Alternate MVS',
16 => 'BeOS',
- 17 => 'Tandem'
+ 17 => 'Tandem',
+ 18 => 'OS/400',
+ 19 => 'OS/X (Darwin)',
);
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
}
public static function ZIPcompressionMethodLookup($index) {
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html
static $ZIPcompressionMethodLookup = array(
0 => 'store',
1 => 'shrink',
@@ -386,7 +465,19 @@ class getid3_zip extends getid3_handler
7 => 'tokenize',
8 => 'deflate',
9 => 'deflate64',
- 10 => 'PKWARE Date Compression Library Imploding'
+ 10 => 'Imploded (old IBM TERSE)',
+ 11 => 'RESERVED[11]',
+ 12 => 'BZIP2',
+ 13 => 'RESERVED[13]',
+ 14 => 'LZMA (EFS)',
+ 15 => 'RESERVED[15]',
+ 16 => 'RESERVED[16]',
+ 17 => 'RESERVED[17]',
+ 18 => 'IBM TERSE (new)',
+ 19 => 'IBM LZ77 z Architecture (PFS)',
+ 96 => 'JPEG recompressed',
+ 97 => 'WavPack compressed',
+ 98 => 'PPMd version I, Rev 1',
);
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
diff --git a/modules/getid3/module.audio-video.matroska.php b/modules/getid3/module.audio-video.matroska.php
index d1359f4f..3c1921fb 100644
--- a/modules/getid3/module.audio-video.matroska.php
+++ b/modules/getid3/module.audio-video.matroska.php
@@ -14,7 +14,6 @@
/////////////////////////////////////////////////////////////////
-// from: http://www.matroska.org/technical/specs/index.html
define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
@@ -207,11 +206,17 @@ define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] --
define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block.
+/**
+* @tutorial http://www.matroska.org/technical/specs/index.html
+*
+* @todo Rewrite EBML parser to reduce it's size and honor default element values
+* @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
+*/
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]
+ 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]
// private parser settings/placeholders
private $EBMLbuffer = '';
@@ -263,10 +268,16 @@ class getid3_matroska extends getid3_handler
case 1: // Video
$track_info['resolution_x'] = $trackarray['PixelWidth'];
$track_info['resolution_y'] = $trackarray['PixelHeight'];
- if (isset($trackarray['DisplayWidth'])) { $track_info['display_x'] = $trackarray['DisplayWidth']; }
- if (isset($trackarray['DisplayHeight'])) { $track_info['display_y'] = $trackarray['DisplayHeight']; }
- if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
- if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
+ $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
+ $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
+ $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
+
+ if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
+ if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; }
+ if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; }
+ if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; }
+ if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
+ if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
switch ($trackarray['CodecID']) {
case 'V_MS/VFW/FOURCC':
@@ -996,19 +1007,7 @@ class getid3_matroska extends getid3_handler
$this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
}
}
- //if (!empty($attachedfile_entry['FileData']) && !empty($attachedfile_entry['FileMimeType']) && preg_match('#^image/#i', $attachedfile_entry['FileMimeType'])) {
- // if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
- // $attachedfile_entry['data'] = $attachedfile_entry['FileData'];
- // $attachedfile_entry['image_mime'] = $attachedfile_entry['FileMimeType'];
- // $info['matroska']['comments']['picture'][] = array('data' => $attachedfile_entry['data'], 'image_mime' => $attachedfile_entry['image_mime'], 'filename' => $attachedfile_entry['FileName']);
- // unset($attachedfile_entry['FileData'], $attachedfile_entry['FileMimeType']);
- // }
- //}
- //if (!empty($attachedfile_entry['image_mime']) && preg_match('#^image/#i', $attachedfile_entry['image_mime'])) {
- // // don't add a second copy of attached images, which are grouped under the standard location [comments][picture]
- //} else {
- $info['matroska']['attachments'][] = $attachedfile_entry;
- //}
+ $info['matroska']['attachments'][] = $attachedfile_entry;
break;
default:
@@ -1737,6 +1736,17 @@ class getid3_matroska extends getid3_handler
return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
}
+ public static function displayUnit($value) {
+ // http://www.matroska.org/technical/specs/index.html#DisplayUnit
+ static $units = array(
+ 0 => 'pixels',
+ 1 => 'centimeters',
+ 2 => 'inches',
+ 3 => 'Display Aspect Ratio');
+
+ return (isset($units[$value]) ? $units[$value] : 'unknown');
+ }
+
private static function getDefaultStreamInfo($streams)
{
foreach (array_reverse($streams) as $stream) {
diff --git a/modules/getid3/module.audio-video.riff.php b/modules/getid3/module.audio-video.riff.php
index d3b7b8cf..ab4b3b61 100644
--- a/modules/getid3/module.audio-video.riff.php
+++ b/modules/getid3/module.audio-video.riff.php
@@ -13,12 +13,18 @@
// Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX //
// dependencies: module.audio.mp3.php //
// module.audio.ac3.php //
-// module.audio.dts.php (optional) //
+// module.audio.dts.php //
// ///
/////////////////////////////////////////////////////////////////
+/**
+* @todo Parse AC-3/DTS audio inside WAVE correctly
+* @todo Rewrite RIFF parser totally
+*/
+
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
class getid3_riff extends getid3_handler
{
@@ -76,64 +82,68 @@ class getid3_riff extends getid3_handler
$nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
- if (!getid3_lib::intValueSupported($nextRIFFoffset + 1024)) {
- $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
- $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
- break;
- } else {
+ try {
$this->fseek($nextRIFFoffset);
- $nextRIFFheader = $this->fread(12);
- if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
- if (substr($nextRIFFheader, 0, 1) == "\x00") {
- // RIFF padded to WORD boundary, we're actually already at the end
- break;
- }
+ } catch (getid3_exception $e) {
+ if ($e->getCode() == 10) {
+ //$this->warning('RIFF parser: '.$e->getMessage());
+ $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
+ $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
+ break;
+ } else {
+ throw $e;
}
- $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
- $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
- $nextRIFFtype = substr($nextRIFFheader, 8, 4);
- $chunkdata = array();
- $chunkdata['offset'] = $nextRIFFoffset + 8;
- $chunkdata['size'] = $nextRIFFsize;
- $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
- switch ($nextRIFFheaderID) {
- case 'RIFF':
- $info['avdataend'] = $nextRIFFoffset;
- if (!getid3_lib::intValueSupported($info['avdataend'])) {
- $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
- $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
- }
- $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
+ }
+ $nextRIFFheader = $this->fread(12);
+ if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
+ if (substr($nextRIFFheader, 0, 1) == "\x00") {
+ // RIFF padded to WORD boundary, we're actually already at the end
+ break;
+ }
+ }
+ $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
+ $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
+ $nextRIFFtype = substr($nextRIFFheader, 8, 4);
+ $chunkdata = array();
+ $chunkdata['offset'] = $nextRIFFoffset + 8;
+ $chunkdata['size'] = $nextRIFFsize;
+ $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
- if (!isset($thisfile_riff[$nextRIFFtype])) {
- $thisfile_riff[$nextRIFFtype] = array();
- }
- $thisfile_riff[$nextRIFFtype][] = $chunkdata;
- break;
+ switch ($nextRIFFheaderID) {
- case 'JUNK':
- // ignore
- $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
- break;
+ case 'RIFF':
+ $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
- case 'IDVX':
- $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
- break;
+ if (!isset($thisfile_riff[$nextRIFFtype])) {
+ $thisfile_riff[$nextRIFFtype] = array();
+ }
+ $thisfile_riff[$nextRIFFtype][] = $chunkdata;
+ break;
- default:
- if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
- $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
- if (substr($DIVXTAG, -7) == 'DIVXTAG') {
- // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
- $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
- $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
- break 2;
- }
+ case 'JUNK':
+ // ignore
+ $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
+ break;
+
+ case 'IDVX':
+ $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
+ break;
+
+ default:
+ if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
+ $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
+ if (substr($DIVXTAG, -7) == 'DIVXTAG') {
+ // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
+ $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
+ $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
+ break 2;
}
- $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
- break 2;
- }
+ }
+ $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
+ break 2;
+
}
+
}
if ($RIFFsubtype == 'WAVE') {
$thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
@@ -178,7 +188,9 @@ class getid3_riff extends getid3_handler
}
$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
- $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
+ if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
+ $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
+ }
$thisfile_audio['lossless'] = false;
if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
@@ -460,6 +472,14 @@ class getid3_riff extends getid3_handler
$thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
$thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
}
+ if (!empty($info['dts'])) {
+ // Dolby DTS files masquerade as PCM-WAV, but they're not
+ $thisfile_audio['wformattag'] = 0x2001;
+ $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
+ $thisfile_audio['lossless'] = false;
+ $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
+ $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
+ }
break;
case 0x08AE: // ClearJump LiteWave
$thisfile_audio['bitrate_mode'] = 'vbr';
@@ -1142,20 +1162,6 @@ class getid3_riff extends getid3_handler
break;
}
- if (isset($thisfile_riff_raw['fmt ']['wFormatTag']) && ($thisfile_riff_raw['fmt ']['wFormatTag'] == 1)) {
- // http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html
- $this->fseek($info['avdataoffset']);
- $FirstFourBytes = $this->fread(4);
- if (preg_match('/^\xFF\x1F\x00\xE8/s', $FirstFourBytes)) {
- // DTSWAV
- $thisfile_audio_dataformat = 'dts';
- } elseif (preg_match('/^\x7F\xFF\x80\x01/s', $FirstFourBytes)) {
- // DTS, but this probably shouldn't happen
- $thisfile_audio_dataformat = 'dts';
- }
- }
-
-
if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
$thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
}
@@ -1283,9 +1289,11 @@ class getid3_riff extends getid3_handler
$maxoffset = min($maxoffset, $info['avdataend']);
while ($this->ftell() < $maxoffset) {
$chunknamesize = $this->fread(8);
- $chunkname = substr($chunknamesize, 0, 4);
- $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
- if (strlen(trim($chunkname, "\x00")) < 4) {
+ //$chunkname = substr($chunknamesize, 0, 4);
+ $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
+ $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
+ //if (strlen(trim($chunkname, "\x00")) < 4) {
+ if (strlen($chunkname) < 4) {
$this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
break;
}
@@ -1392,90 +1400,95 @@ class getid3_riff extends getid3_handler
$info['avdataoffset'] = $this->ftell();
$info['avdataend'] = $info['avdataoffset'] + $chunksize;
- $RIFFdataChunkContentsTest = $this->fread(36);
-
- if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
+ $testData = $this->fread(36);
+ if ($testData === '') {
+ break;
+ }
+ if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
// Probably is MP3 data
- if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
+ if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
- $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
- $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
+ $getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp);
- $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false);
+ $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
if (empty($getid3_temp->info['error'])) {
- $info['mpeg'] = $getid3_temp->info['mpeg'];
$info['audio'] = $getid3_temp->info['audio'];
+ $info['mpeg'] = $getid3_temp->info['mpeg'];
}
unset($getid3_temp, $getid3_mp3);
}
- } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == getid3_ac3::syncword)) {
+ } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
// This is probably AC-3 data
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
- $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
- $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
+ if ($isRegularAC3) {
+ $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
+ $getid3_temp->info['avdataend'] = $info['avdataend'];
+ }
$getid3_ac3 = new getid3_ac3($getid3_temp);
- $getid3_ac3->Analyze();
+ if ($isRegularAC3) {
+ $getid3_ac3->Analyze();
+ } else {
+ // Dolby Digital WAV
+ // AC-3 content, but not encoded in same format as normal AC-3 file
+ // For one thing, byte order is swapped
+ $ac3_data = '';
+ for ($i = 0; $i < 28; $i += 2) {
+ $ac3_data .= substr($testData, 8 + $i + 1, 1);
+ $ac3_data .= substr($testData, 8 + $i + 0, 1);
+ }
+ $getid3_ac3->AnalyzeString($ac3_data);
+ }
+
if (empty($getid3_temp->info['error'])) {
- $info['audio'] = $getid3_temp->info['audio'];
- $info['ac3'] = $getid3_temp->info['ac3'];
- $info['warning'] = $getid3_temp->info['warning'];
+ $info['audio'] = $getid3_temp->info['audio'];
+ $info['ac3'] = $getid3_temp->info['ac3'];
+ if (!empty($getid3_temp->info['warning'])) {
+ foreach ($getid3_temp->info['warning'] as $newerror) {
+ $this->warning('getid3_ac3() says: ['.$newerror.']');
+ }
+ }
}
unset($getid3_temp, $getid3_ac3);
- } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == getid3_ac3::syncword)) {
-
- // Dolby Digital WAV
- // AC-3 content, but not encoded in same format as normal AC-3 file
- // For one thing, byte order is swapped
- if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) { // ok to use tmpfile here - only 56 bytes
- if ($fd_temp = fopen($RIFFtempfilename, 'wb')) {
- for ($i = 0; $i < 28; $i += 2) {
- // swap byte order
- fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
- fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
- }
- fclose($fd_temp);
+ } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
- $getid3_temp = new getID3();
- $getid3_temp->openfile($RIFFtempfilename);
- $getid3_temp->info['avdataend'] = 20;
- $getid3_ac3 = new getid3_ac3($getid3_temp);
- $getid3_ac3->Analyze();
- if (empty($getid3_temp->info['error'])) {
- $info['audio'] = $getid3_temp->info['audio'];
- $info['ac3'] = $getid3_temp->info['ac3'];
- $info['warning'] = $getid3_temp->info['warning'];
- } else {
- $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): '.implode(';', $getid3_temp->info['error']);
+ // This is probably DTS data
+ $getid3_temp = new getID3();
+ $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
+ $getid3_dts = new getid3_dts($getid3_temp);
+ $getid3_dts->Analyze();
+ if (empty($getid3_temp->info['error'])) {
+ $info['audio'] = $getid3_temp->info['audio'];
+ $info['dts'] = $getid3_temp->info['dts'];
+ $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
+ if (!empty($getid3_temp->info['warning'])) {
+ foreach ($getid3_temp->info['warning'] as $newerror) {
+ $this->warning('getid3_dts() says: ['.$newerror.']');
}
- unset($getid3_ac3, $getid3_temp);
- } else {
- $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
}
- unlink($RIFFtempfilename);
- } else {
- $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
}
- } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) {
+ unset($getid3_temp, $getid3_dts);
+
+ } elseif (substr($testData, 0, 4) == 'wvpk') {
// This is WavPack data
- $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
- $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
- self::parseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28));
+ $info['wavpack']['offset'] = $info['avdataoffset'];
+ $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
+ $this->parseWavPackHeader(substr($testData, 8, 28));
} else {
-
// This is some other kind of data (quite possibly just PCM)
// do nothing special, just skip it
-
}
- $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
+ $nextoffset = $info['avdataend'];
$this->fseek($nextoffset);
break;
@@ -1628,7 +1641,8 @@ class getid3_riff extends getid3_handler
'ISTR'=>'starring',
'ITCH'=>'encoded_by',
'IWEB'=>'url',
- 'IWRI'=>'writer'
+ 'IWRI'=>'writer',
+ '____'=>'comment',
);
foreach ($RIFFinfoKeyLookup as $key => $value) {
if (isset($RIFFinfoArray[$key])) {
@@ -1787,8 +1801,8 @@ class getid3_riff extends getid3_handler
19 => 'Sci Fi',
20 => 'Thriller',
21 => 'Western',
- );
- static $DIVXTAGrating = array(
+ ),
+ $DIVXTAGrating = array(
0 => 'Unrated',
1 => 'G',
2 => 'PG',
@@ -1818,6 +1832,10 @@ class getid3_riff extends getid3_handler
}
}
+ foreach ($parsed as $tag => $value) {
+ $parsed[$tag] = array($value);
+ }
+
return $parsed;
}
diff --git a/modules/getid3/module.audio.ac3.php b/modules/getid3/module.audio.ac3.php
index be3064f1..9834feb5 100644
--- a/modules/getid3/module.audio.ac3.php
+++ b/modules/getid3/module.audio.ac3.php
@@ -272,20 +272,17 @@ class getid3_ac3 extends getid3_handler
}
public static function audioCodingModeLookup($acmod) {
- static $audioCodingModeLookup = array();
- if (empty($audioCodingModeLookup)) {
- // array(channel configuration, # channels (not incl LFE), channel order)
- $audioCodingModeLookup = array (
- 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
- 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
- 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
- 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
- 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
- 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
- 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
- 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR')
- );
- }
+ // array(channel configuration, # channels (not incl LFE), channel order)
+ static $audioCodingModeLookup = array (
+ 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
+ 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
+ 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
+ 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
+ 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
+ 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
+ 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
+ 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'),
+ );
return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
}
@@ -326,7 +323,7 @@ class getid3_ac3 extends getid3_handler
}
public static function channelsEnabledLookup($acmod, $lfeon) {
- $channelsEnabledLookup = array(
+ $lookup = array(
'ch1'=>(bool) ($acmod == 0),
'ch2'=>(bool) ($acmod == 0),
'left'=>(bool) ($acmod > 1),
@@ -339,15 +336,15 @@ class getid3_ac3 extends getid3_handler
switch ($acmod) {
case 4:
case 5:
- $channelsEnabledLookup['surround_mono'] = true;
+ $lookup['surround_mono'] = true;
break;
case 6:
case 7:
- $channelsEnabledLookup['surround_left'] = true;
- $channelsEnabledLookup['surround_right'] = true;
+ $lookup['surround_left'] = true;
+ $lookup['surround_right'] = true;
break;
}
- return $channelsEnabledLookup;
+ return $lookup;
}
public static function heavyCompression($compre) {
diff --git a/modules/getid3/module.audio.dss.php b/modules/getid3/module.audio.dss.php
index 5e1ceacb..4719d53b 100644
--- a/modules/getid3/module.audio.dss.php
+++ b/modules/getid3/module.audio.dss.php
@@ -23,36 +23,41 @@ class getid3_dss extends getid3_handler
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
$DSSheader = fread($this->getid3->fp, 1256);
- if (!preg_match('#^(\x02|\x03)dss#', $DSSheader)) {
- $info['error'][] = 'Expecting "[02-03] 64 73 73" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
+ if (!preg_match('#^(\x02|\x03)ds[s2]#', $DSSheader)) {
+ $info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
return false;
}
// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
-
- // shortcut
+ $info['encoding'] = 'ISO-8859-1'; // not certain, but assumed
$info['dss'] = array();
- $thisfile_dss = &$info['dss'];
$info['fileformat'] = 'dss';
- $info['audio']['dataformat'] = 'dss';
+ $info['mime_type'] = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2"
+ $info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2"
$info['audio']['bitrate_mode'] = 'cbr';
- //$thisfile_dss['encoding'] = 'ISO-8859-1';
-
- $thisfile_dss['version'] = ord(substr($DSSheader, 0, 1));
- $thisfile_dss['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
- $thisfile_dss['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
- //$thisfile_dss['length'] = intval(substr($DSSheader, 62, 6)); // I thought time was in seconds, it's actually HHMMSS
- $thisfile_dss['length'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2));
- $thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1));
- $thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100));
+ $info['dss']['version'] = ord(substr($DSSheader, 0, 1));
+ $info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
+ $info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
+ // 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
+ $info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
+ $info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
+ $info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
+ $info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
+ $info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
+ $info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
//$info['audio']['bits_per_sample'] = ?;
//$info['audio']['sample_rate'] = ?;
$info['audio']['channels'] = 1;
- $info['playtime_seconds'] = $thisfile_dss['length'];
+ $info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
+ if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
+ // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
+ $info['playtime_seconds'] = $info['dss']['playtime_sec'];
+ $this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
+ }
$info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
return true;
diff --git a/modules/getid3/module.audio.dts.php b/modules/getid3/module.audio.dts.php
index 5b910940..79982ccc 100644
--- a/modules/getid3/module.audio.dts.php
+++ b/modules/getid3/module.audio.dts.php
@@ -14,64 +14,102 @@
/////////////////////////////////////////////////////////////////
+/**
+* @tutorial http://wiki.multimedia.cx/index.php?title=DTS
+*/
class getid3_dts extends getid3_handler
{
+ /**
+ * Default DTS syncword used in native .cpt or .dts formats
+ */
const syncword = "\x7F\xFE\x80\x01";
- public function Analyze() {
- $info = &$this->getid3->info;
+ private $readBinDataOffset = 0;
- // Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)"
- // (http://pda.etsi.org/pda/queryform.asp)
- // With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/
+ /**
+ * 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
+ public function Analyze() {
+ $info = &$this->getid3->info;
$info['fileformat'] = 'dts';
$this->fseek($info['avdataoffset']);
- $DTSheader = $this->fread(16);
-
- if (strpos($DTSheader, self::syncword) === 0) {
- $info['dts']['raw']['magic'] = self::syncword;
- $offset = 4;
- } else {
- if (!$this->isDependencyFor('matroska')) {
- unset($info['fileformat']);
- return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DTSheader, 0, 4)).'"');
+ $DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
+
+ // check syncword
+ $sync = substr($DTSheader, 0, 4);
+ if (($encoding = array_search($sync, self::$syncwords)) !== false) {
+
+ $info['dts']['raw']['magic'] = $sync;
+ $this->readBinDataOffset = 32;
+
+ } elseif ($this->isDependencyFor('matroska')) {
+
+ // Matroska contains DTS without syncword encoded as raw big-endian format
+ $encoding = 0;
+ $this->readBinDataOffset = 0;
+
+ } else {
+
+ unset($info['fileformat']);
+ return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
+
+ }
+
+ // decode header
+ $fhBS = '';
+ for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
+ switch ($encoding) {
+ case 0: // raw big-endian
+ $fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) );
+ break;
+ case 1: // raw little-endian
+ $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
+ break;
+ case 2: // 14-bit big-endian
+ $fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14);
+ break;
+ case 3: // 14-bit little-endian
+ $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
+ break;
}
- $offset = 0;
}
- $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, $offset, 12));
- $bsOffset = 0;
- $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, $bsOffset, 5);
- $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, $bsOffset, 7);
- $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, $bsOffset, 14);
- $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, $bsOffset, 6);
- $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, $bsOffset, 4);
- $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, $bsOffset, 5);
- $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, $bsOffset, 3);
- $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, $bsOffset, 2);
- $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
+ $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
+ $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
+ $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
+ $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
+ $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
+ $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
+ $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
+ $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
+ $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
if ($info['dts']['flags']['crc_present']) {
- $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, $bsOffset, 16);
+ $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
}
- $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, $bsOffset, 4);
- $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, $bsOffset, 2);
- $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, $bsOffset, 2);
- $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
- $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, $bsOffset, 4);
+ $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
+ $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
+ $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
+ $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
+ $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
$info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
@@ -90,16 +128,20 @@ class getid3_dts extends getid3_handler
$info['audio']['sample_rate'] = $info['dts']['sample_rate'];
$info['audio']['channels'] = $info['dts']['channels'];
$info['audio']['bitrate'] = $info['dts']['bitrate'];
- if (isset($info['avdataend'])) {
+ if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
+ if (($encoding == 2) || ($encoding == 3)) {
+ // 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
+ $info['playtime_seconds'] *= (14 / 16);
+ }
}
-
return true;
}
- private function readBinData($bin, &$offset, $length) {
- $data = substr($bin, $offset, $length);
- $offset += $length;
+ private function readBinData($bin, $length) {
+ $data = substr($bin, $this->readBinDataOffset, $length);
+ $this->readBinDataOffset += $length;
+
return bindec($data);
}
diff --git a/modules/getid3/module.audio.flac.php b/modules/getid3/module.audio.flac.php
index ec8136fe..6b9598c7 100644
--- a/modules/getid3/module.audio.flac.php
+++ b/modules/getid3/module.audio.flac.php
@@ -16,6 +16,9 @@
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
+/**
+* @tutorial http://flac.sourceforge.net/format.html
+*/
class getid3_flac extends getid3_handler
{
const syncword = 'fLaC';
@@ -23,8 +26,6 @@ class getid3_flac extends getid3_handler
public function Analyze() {
$info = &$this->getid3->info;
- // http://flac.sourceforge.net/format.html
-
$this->fseek($info['avdataoffset']);
$StreamMarker = $this->fread(4);
if ($StreamMarker != self::syncword) {
@@ -146,7 +147,7 @@ class getid3_flac extends getid3_handler
if ($info['flac']['uncompressed_audio_bytes'] == 0) {
return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
}
- if (!$this->isDependencyFor('matroska')) {
+ if (!empty($info['flac']['compressed_audio_bytes'])) {
$info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
}
}
@@ -357,7 +358,7 @@ class getid3_flac extends getid3_handler
$picture['data'] = $this->fread($data_length);
} else {
$picture['data'] = $this->saveAttachment(
- $picture['type'].'_'.$this->ftell(),
+ str_replace('/', '_', $picture['type']).'_'.$this->ftell(),
$this->ftell(),
$data_length,
$picture['image_mime']);
@@ -384,30 +385,30 @@ class getid3_flac extends getid3_handler
public static function applicationIDLookup($applicationid) {
// http://flac.sourceforge.net/id.html
static $lookup = array(
- 0x41544348 => 'FlacFile', // "ATCH"
- 0x42534F4C => 'beSolo', // "BSOL"
- 0x42554753 => 'Bugs Player', // "BUGS"
- 0x43756573 => 'GoldWave cue points (specification)', // "Cues"
- 0x46696361 => 'CUE Splitter', // "Fica"
- 0x46746F6C => 'flac-tools', // "Ftol"
- 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
- 0x4D505345 => 'MP3 Stream Editor', // "MPSE"
- 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
- 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
- 0x5346464C => 'Sound Font FLAC', // "SFFL"
- 0x534F4E59 => 'Sony Creative Software', // "SONY"
- 0x5351455A => 'flacsqueeze', // "SQEZ"
- 0x54745776 => 'TwistedWave', // "TtWv"
- 0x55495453 => 'UITS Embedding tools', // "UITS"
- 0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
- 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
- 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
- 0x71667374 => 'QFLAC Studio', // "qfst"
- 0x72696666 => 'FLAC RIFF chunk storage', // "riff"
- 0x74756E65 => 'TagTuner', // "tune"
- 0x78626174 => 'XBAT', // "xbat"
+ 0x41544348 => 'FlacFile', // "ATCH"
+ 0x42534F4C => 'beSolo', // "BSOL"
+ 0x42554753 => 'Bugs Player', // "BUGS"
+ 0x43756573 => 'GoldWave cue points (specification)', // "Cues"
+ 0x46696361 => 'CUE Splitter', // "Fica"
+ 0x46746F6C => 'flac-tools', // "Ftol"
+ 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
+ 0x4D505345 => 'MP3 Stream Editor', // "MPSE"
+ 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
+ 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
+ 0x5346464C => 'Sound Font FLAC', // "SFFL"
+ 0x534F4E59 => 'Sony Creative Software', // "SONY"
+ 0x5351455A => 'flacsqueeze', // "SQEZ"
+ 0x54745776 => 'TwistedWave', // "TtWv"
+ 0x55495453 => 'UITS Embedding tools', // "UITS"
+ 0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
+ 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
+ 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
+ 0x71667374 => 'QFLAC Studio', // "qfst"
+ 0x72696666 => 'FLAC RIFF chunk storage', // "riff"
+ 0x74756E65 => 'TagTuner', // "tune"
+ 0x78626174 => 'XBAT', // "xbat"
0x786D6364 => 'xmcd', // "xmcd"
- );
+ );
return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
}
diff --git a/modules/getid3/module.graphic.jpg.php b/modules/getid3/module.graphic.jpg.php
index 5663a2f5..3db65438 100644
--- a/modules/getid3/module.graphic.jpg.php
+++ b/modules/getid3/module.graphic.jpg.php
@@ -31,7 +31,8 @@ class getid3_jpg extends getid3_handler
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
$imageinfo = array();
- list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($this->getid3->fp, $info['filesize']), $imageinfo);
+ //list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($this->getid3->fp, $info['filesize']), $imageinfo);
+ list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474
if (isset($imageinfo['APP13'])) {