1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at http://getid3.sourceforge.net                 //
5//            or http://www.getid3.org                         //
6/////////////////////////////////////////////////////////////////
7//                                                             //
8// Please see readme.txt for more information                  //
9//                                                            ///
10/////////////////////////////////////////////////////////////////
11
12// Defines
13define('GETID3_VERSION', '1.7.8b2');
14define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
15
16
17
18class getID3
19{
20	// public: Settings
21	var $encoding        = 'ISO-8859-1';   // CASE SENSITIVE! - i.e. (must be supported by iconv())
22	                                       // Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
23
24	var $encoding_id3v1  = 'ISO-8859-1';   // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN'
25
26	var $tempdir         = '*';            // default '*' should use system temp dir
27
28	// public: Optional tag checks - disable for speed.
29	var $option_tag_id3v1         = true;  // Read and process ID3v1 tags
30	var $option_tag_id3v2         = true;  // Read and process ID3v2 tags
31	var $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
32	var $option_tag_apetag        = true;  // Read and process APE tags
33	var $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
34	var $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
35
36	// public: Optional tag/comment calucations
37	var $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
38
39	// public: Optional calculations
40	var $option_md5_data          = false; // Get MD5 sum of data part - slow
41	var $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
42	var $option_sha1_data         = false; // Get SHA1 sum of data part - slow
43	var $option_max_2gb_check     = true;  // Check whether file is larger than 2 Gb and thus not supported by PHP
44
45	// private
46	var $filename;
47
48
49	// public: constructor
50	function getID3()
51	{
52
53		$this->startup_error   = '';
54		$this->startup_warning = '';
55
56		// Check for PHP version >= 4.2.0
57		if (phpversion() < '4.2.0') {
58		    $this->startup_error .= 'getID3() requires PHP v4.2.0 or higher - you are running v'.phpversion();
59		}
60
61		// Check memory
62		$memory_limit = ini_get('memory_limit');
63		if (eregi('([0-9]+)M', $memory_limit, $matches)) {
64			// could be stored as "16M" rather than 16777216 for example
65			$memory_limit = $matches[1] * 1048576;
66		}
67		if ($memory_limit <= 0) {
68			// memory limits probably disabled
69		} elseif ($memory_limit <= 3145728) {
70	    	$this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini';
71		} elseif ($memory_limit <= 12582912) {
72	    	$this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
73		}
74
75		// Check safe_mode off
76		if ((bool) ini_get('safe_mode')) {
77		    $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
78		}
79
80
81		// define a constant rather than looking up every time it is needed
82		if (!defined('GETID3_OS_ISWINDOWS')) {
83			if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
84				define('GETID3_OS_ISWINDOWS', true);
85			} else {
86				define('GETID3_OS_ISWINDOWS', false);
87			}
88		}
89
90		// Get base path of getID3() - ONCE
91		if (!defined('GETID3_INCLUDEPATH')) {
92			foreach (get_included_files() as $key => $val) {
93				if (basename($val) == 'getid3.php') {
94					define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
95					break;
96				}
97			}
98		}
99
100		// Load support library
101		if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
102			$this->startup_error .= 'getid3.lib.php is missing or corrupt';
103		}
104
105
106		// Needed for Windows only:
107		// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
108		//   as well as other helper functions such as head, tail, md5sum, etc
109		// IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent
110		//   ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/"
111		// IMPORTANT: This path must include the trailing slash
112		if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
113
114			$helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
115
116			if (!is_dir($helperappsdir)) {
117				$this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
118			} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
119				$DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
120				foreach ($DirPieces as $key => $value) {
121					if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) {
122						if (strpos($value, '.') > 8) {
123							$value = substr($value, 0, 6).'~1';
124						}
125					} elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) {
126						$value = substr($value, 0, 6).'~1';
127					}
128					$DirPieces[$key] = strtoupper($value);
129				}
130				$this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(DIRECTORY_SEPARATOR, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names.';
131			}
132			define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR);
133		}
134
135	}
136
137
138	// public: setOption
139	function setOption($optArray) {
140		if (!is_array($optArray) || empty($optArray)) {
141			return false;
142		}
143		foreach ($optArray as $opt => $val) {
144			if (isset($this, $opt) === false) {
145				continue;
146			}
147			$this->$opt = $val;
148		}
149		return true;
150	}
151
152
153	// public: analyze file - replaces GetAllFileInfo() and GetTagOnly()
154	function analyze($filename) {
155
156		if (!empty($this->startup_error)) {
157			return $this->error($this->startup_error);
158		}
159		if (!empty($this->startup_warning)) {
160			$this->warning($this->startup_warning);
161		}
162
163		// init result array and set parameters
164		$this->info = array();
165		$this->info['GETID3_VERSION'] = GETID3_VERSION;
166
167		// Check encoding/iconv support
168		if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
169			$errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
170			if (GETID3_OS_ISWINDOWS) {
171				$errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
172			} else {
173				$errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
174			}
175	    	return $this->error($errormessage);
176		}
177
178		// Disable magic_quotes_runtime, if neccesary
179		$old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime
180		if ($old_magic_quotes_runtime) {
181			set_magic_quotes_runtime(0);                        // turn off magic_quotes_runtime
182			if (get_magic_quotes_runtime()) {
183				return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled');
184			}
185		}
186
187		// remote files not supported
188		if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
189			return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
190		}
191
192		// open local file
193		if (!$fp = @fopen($filename, 'rb')) {
194			return $this->error('Could not open file "'.$filename.'"');
195		}
196
197		// set parameters
198		$this->info['filesize'] = filesize($filename);
199
200		// option_max_2gb_check
201		if ($this->option_max_2gb_check) {
202			// PHP doesn't support integers larger than 31-bit (~2GB)
203			// filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
204			// ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
205			fseek($fp, 0, SEEK_END);
206			if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) ||
207				($this->info['filesize'] < 0) ||
208				(ftell($fp) < 0)) {
209					unset($this->info['filesize']);
210					fclose($fp);
211					return $this->error('File is most likely larger than 2GB and is not supported by PHP');
212			}
213		}
214
215		// set more parameters
216		$this->info['avdataoffset']        = 0;
217		$this->info['avdataend']           = $this->info['filesize'];
218		$this->info['fileformat']          = '';                // filled in later
219		$this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
220		$this->info['video']['dataformat'] = '';                // filled in later, unset if not used
221		$this->info['tags']                = array();           // filled in later, unset if not used
222		$this->info['error']               = array();           // filled in later, unset if not used
223		$this->info['warning']             = array();           // filled in later, unset if not used
224		$this->info['comments']            = array();           // filled in later, unset if not used
225		$this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
226
227		// set redundant parameters - might be needed in some include file
228		$this->info['filename']            = basename($filename);
229		$this->info['filepath']            = str_replace('\\', '/', realpath(dirname($filename)));
230		$this->info['filenamepath']        = $this->info['filepath'].'/'.$this->info['filename'];
231
232
233		// handle ID3v2 tag - done first - already at beginning of file
234		// ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
235		if ($this->option_tag_id3v2) {
236
237			$GETID3_ERRORARRAY = &$this->info['warning'];
238			if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) {
239				$tag = new getid3_id3v2($fp, $this->info);
240				unset($tag);
241			}
242
243		} else {
244
245			fseek($fp, 0, SEEK_SET);
246			$header = fread($fp, 10);
247			if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
248				$this->info['id3v2']['header']           = true;
249				$this->info['id3v2']['majorversion']     = ord($header{3});
250				$this->info['id3v2']['minorversion']     = ord($header{4});
251				$this->info['id3v2']['headerlength']     = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
252
253				$this->info['id3v2']['tag_offset_start'] = 0;
254				$this->info['id3v2']['tag_offset_end']   = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
255				$this->info['avdataoffset']              = $this->info['id3v2']['tag_offset_end'];
256			}
257
258		}
259
260
261		// handle ID3v1 tag
262		if ($this->option_tag_id3v1) {
263			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) {
264				return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
265			}
266			$tag = new getid3_id3v1($fp, $this->info);
267			unset($tag);
268		}
269
270		// handle APE tag
271		if ($this->option_tag_apetag) {
272			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) {
273				return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
274			}
275			$tag = new getid3_apetag($fp, $this->info);
276			unset($tag);
277		}
278
279		// handle lyrics3 tag
280		if ($this->option_tag_lyrics3) {
281			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) {
282				return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
283			}
284			$tag = new getid3_lyrics3($fp, $this->info);
285			unset($tag);
286		}
287
288		// read 32 kb file data
289		fseek($fp, $this->info['avdataoffset'], SEEK_SET);
290		$formattest = fread($fp, 32774);
291
292		// determine format
293		$determined_format = $this->GetFileFormat($formattest, $filename);
294
295		// unable to determine file format
296		if (!$determined_format) {
297			fclose($fp);
298			return $this->error('unable to determine file format');
299		}
300
301		// check for illegal ID3 tags
302		if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
303			if ($determined_format['fail_id3'] === 'ERROR') {
304				fclose($fp);
305				return $this->error('ID3 tags not allowed on this file type.');
306			} elseif ($determined_format['fail_id3'] === 'WARNING') {
307				$this->info['warning'][] = 'ID3 tags not allowed on this file type.';
308			}
309		}
310
311		// check for illegal APE tags
312		if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
313			if ($determined_format['fail_ape'] === 'ERROR') {
314				fclose($fp);
315				return $this->error('APE tags not allowed on this file type.');
316			} elseif ($determined_format['fail_ape'] === 'WARNING') {
317				$this->info['warning'][] = 'APE tags not allowed on this file type.';
318			}
319		}
320
321		// set mime type
322		$this->info['mime_type'] = $determined_format['mime_type'];
323
324		// supported format signature pattern detected, but module deleted
325		if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
326			fclose($fp);
327			return $this->error('Format not supported, module, '.$determined_format['include'].', was removed.');
328		}
329
330		// module requires iconv support
331        if (!function_exists('iconv') && @$determined_format['iconv_req']) {
332		    return $this->error('iconv support is required for this module ('.$determined_format['include'].').');
333		}
334
335		// include module
336		include_once(GETID3_INCLUDEPATH.$determined_format['include']);
337
338		// instantiate module class
339		$class_name = 'getid3_'.$determined_format['module'];
340		if (!class_exists($class_name)) {
341			return $this->error('Format not supported, module, '.$determined_format['include'].', is corrupt.');
342		}
343		if (isset($determined_format['option'])) {
344			$class = new $class_name($fp, $this->info, $determined_format['option']);
345		} else {
346			$class = new $class_name($fp, $this->info);
347		}
348		unset($class);
349
350		// close file
351		fclose($fp);
352
353		// process all tags - copy to 'tags' and convert charsets
354		if ($this->option_tags_process) {
355			$this->HandleAllTags();
356		}
357
358		// perform more calculations
359		if ($this->option_extra_info) {
360			$this->ChannelsBitratePlaytimeCalculations();
361			$this->CalculateCompressionRatioVideo();
362			$this->CalculateCompressionRatioAudio();
363			$this->CalculateReplayGain();
364			$this->ProcessAudioStreams();
365		}
366
367		// get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
368		if ($this->option_md5_data) {
369			// do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
370			if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
371				$this->getHashdata('md5');
372			}
373		}
374
375		// get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
376		if ($this->option_sha1_data) {
377			$this->getHashdata('sha1');
378		}
379
380		// remove undesired keys
381		$this->CleanUp();
382
383		// restore magic_quotes_runtime setting
384		set_magic_quotes_runtime($old_magic_quotes_runtime);
385
386		// return info array
387		return $this->info;
388	}
389
390
391	// private: error handling
392	function error($message) {
393
394		$this->CleanUp();
395
396		$this->info['error'][] = $message;
397		return $this->info;
398	}
399
400
401	// private: warning handling
402	function warning($message) {
403		$this->info['warning'][] = $message;
404		return true;
405	}
406
407
408	// private: CleanUp
409	function CleanUp() {
410
411		// remove possible empty keys
412		$AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
413		foreach ($AVpossibleEmptyKeys as $dummy => $key) {
414			if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
415				unset($this->info['audio'][$key]);
416			}
417			if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
418				unset($this->info['video'][$key]);
419			}
420		}
421
422		// remove empty root keys
423		if (!empty($this->info)) {
424			foreach ($this->info as $key => $value) {
425				if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
426					unset($this->info[$key]);
427				}
428			}
429		}
430
431		// remove meaningless entries from unknown-format files
432		if (empty($this->info['fileformat'])) {
433			if (isset($this->info['avdataoffset'])) {
434				unset($this->info['avdataoffset']);
435			}
436			if (isset($this->info['avdataend'])) {
437				unset($this->info['avdataend']);
438			}
439		}
440	}
441
442
443	// return array containing information about all supported formats
444	function GetFileFormatArray() {
445		static $format_info = array();
446		if (empty($format_info)) {
447			$format_info = array(
448
449				// Audio formats
450
451				// AC-3   - audio      - Dolby AC-3 / Dolby Digital
452				'ac3'  => array(
453							'pattern'   => '^\x0B\x77',
454							'group'     => 'audio',
455							'module'    => 'ac3',
456							'mime_type' => 'audio/ac3',
457						),
458
459				// AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
460				'adif' => array(
461							'pattern'   => '^ADIF',
462							'group'     => 'audio',
463							'module'    => 'aac',
464							'option'    => 'adif',
465							'mime_type' => 'application/octet-stream',
466							'fail_ape'  => 'WARNING',
467						),
468
469
470				// AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
471				'adts' => array(
472							'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
473							'group'     => 'audio',
474							'module'    => 'aac',
475							'option'    => 'adts',
476							'mime_type' => 'application/octet-stream',
477							'fail_ape'  => 'WARNING',
478						),
479
480
481				// AU   - audio       - NeXT/Sun AUdio (AU)
482				'au'   => array(
483							'pattern'   => '^\.snd',
484							'group'     => 'audio',
485							'module'    => 'au',
486							'mime_type' => 'audio/basic',
487						),
488
489				// AVR  - audio       - Audio Visual Research
490				'avr'  => array(
491							'pattern'   => '^2BIT',
492							'group'     => 'audio',
493							'module'    => 'avr',
494							'mime_type' => 'application/octet-stream',
495						),
496
497				// BONK - audio       - Bonk v0.9+
498				'bonk' => array(
499							'pattern'   => '^\x00(BONK|INFO|META| ID3)',
500							'group'     => 'audio',
501							'module'    => 'bonk',
502							'mime_type' => 'audio/xmms-bonk',
503						),
504
505				// DTS  - audio       - Dolby Theatre System
506				'dts'  => array(
507							'pattern'   => '^\x7F\xFE\x80\x01',
508							'group'     => 'audio',
509							'module'    => 'dts',
510							'mime_type' => 'audio/dts',
511						),
512
513				// FLAC - audio       - Free Lossless Audio Codec
514				'flac' => array(
515							'pattern'   => '^fLaC',
516							'group'     => 'audio',
517							'module'    => 'flac',
518							'mime_type' => 'audio/x-flac',
519						),
520
521				// LA   - audio       - Lossless Audio (LA)
522				'la'   => array(
523							'pattern'   => '^LA0[2-4]',
524							'group'     => 'audio',
525							'module'    => 'la',
526							'mime_type' => 'application/octet-stream',
527						),
528
529				// LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
530				'lpac' => array(
531							'pattern'   => '^LPAC',
532							'group'     => 'audio',
533							'module'    => 'lpac',
534							'mime_type' => 'application/octet-stream',
535						),
536
537				// MIDI - audio       - MIDI (Musical Instrument Digital Interface)
538				'midi' => array(
539							'pattern'   => '^MThd',
540							'group'     => 'audio',
541							'module'    => 'midi',
542							'mime_type' => 'audio/midi',
543						),
544
545				// MAC  - audio       - Monkey's Audio Compressor
546				'mac'  => array(
547							'pattern'   => '^MAC ',
548							'group'     => 'audio',
549							'module'    => 'monkey',
550							'mime_type' => 'application/octet-stream',
551						),
552
553				// MOD  - audio       - MODule (assorted sub-formats)
554				'mod'  => array(
555							'pattern'   => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)',
556							'group'     => 'audio',
557							'module'    => 'mod',
558							'option'    => 'mod',
559							'mime_type' => 'audio/mod',
560						),
561
562				// MOD  - audio       - MODule (Impulse Tracker)
563				'it'   => array(
564							'pattern'   => '^IMPM',
565							'group'     => 'audio',
566							'module'    => 'mod',
567							'option'    => 'it',
568							'mime_type' => 'audio/it',
569						),
570
571				// MOD  - audio       - MODule (eXtended Module, various sub-formats)
572				'xm'   => array(
573							'pattern'   => '^Extended Module',
574							'group'     => 'audio',
575							'module'    => 'mod',
576							'option'    => 'xm',
577							'mime_type' => 'audio/xm',
578						),
579
580				// MOD  - audio       - MODule (ScreamTracker)
581				's3m'  => array(
582							'pattern'   => '^.{44}SCRM',
583							'group'     => 'audio',
584							'module'    => 'mod',
585							'option'    => 's3m',
586							'mime_type' => 'audio/s3m',
587						),
588
589				// MPC  - audio       - Musepack / MPEGplus
590				'mpc'  => array(
591							'pattern'   => '^(MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
592							'group'     => 'audio',
593							'module'    => 'mpc',
594							'mime_type' => 'audio/x-musepack',
595						),
596
597				// MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
598				'mp3'  => array(
599							'pattern'   => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]',
600							'group'     => 'audio',
601							'module'    => 'mp3',
602							'mime_type' => 'audio/mpeg',
603						),
604
605				// OFR  - audio       - OptimFROG
606				'ofr'  => array(
607							'pattern'   => '^(\*RIFF|OFR)',
608							'group'     => 'audio',
609							'module'    => 'optimfrog',
610							'mime_type' => 'application/octet-stream',
611						),
612
613				// RKAU - audio       - RKive AUdio compressor
614				'rkau' => array(
615							'pattern'   => '^RKA',
616							'group'     => 'audio',
617							'module'    => 'rkau',
618							'mime_type' => 'application/octet-stream',
619						),
620
621				// SHN  - audio       - Shorten
622				'shn'  => array(
623							'pattern'   => '^ajkg',
624							'group'     => 'audio',
625							'module'    => 'shorten',
626							'mime_type' => 'audio/xmms-shn',
627							'fail_id3'  => 'ERROR',
628							'fail_ape'  => 'ERROR',
629						),
630
631				// TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
632				'tta'  => array(
633							'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
634							'group'     => 'audio',
635							'module'    => 'tta',
636							'mime_type' => 'application/octet-stream',
637						),
638
639				// VOC  - audio       - Creative Voice (VOC)
640				'voc'  => array(
641							'pattern'   => '^Creative Voice File',
642							'group'     => 'audio',
643							'module'    => 'voc',
644							'mime_type' => 'audio/voc',
645						),
646
647				// VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
648				'vqf'  => array(
649							'pattern'   => '^TWIN',
650							'group'     => 'audio',
651							'module'    => 'vqf',
652							'mime_type' => 'application/octet-stream',
653						),
654
655				// WV  - audio        - WavPack (v4.0+)
656				'wv'   => array(
657							'pattern'   => '^wvpk',
658							'group'     => 'audio',
659							'module'    => 'wavpack',
660							'mime_type' => 'application/octet-stream',
661						),
662
663
664				// Audio-Video formats
665
666				// ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
667				'asf'  => array(
668							'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
669							'group'     => 'audio-video',
670							'module'    => 'asf',
671							'mime_type' => 'video/x-ms-asf',
672							'iconv_req' => false,
673						),
674
675				// BINK - audio/video - Bink / Smacker
676				'bink' => array(
677							'pattern'   => '^(BIK|SMK)',
678							'group'     => 'audio-video',
679							'module'    => 'bink',
680							'mime_type' => 'application/octet-stream',
681						),
682
683				// FLV  - audio/video - FLash Video
684				'flv' => array(
685							'pattern'   => '^FLV\x01',
686							'group'     => 'audio-video',
687							'module'    => 'flv',
688							'mime_type' => 'video/x-flv',
689						),
690
691				// MKAV - audio/video - Mastroka
692				'matroska' => array(
693							'pattern'   => '^\x1A\x45\xDF\xA3',
694							'group'     => 'audio-video',
695							'module'    => 'matroska',
696							'mime_type' => 'application/octet-stream',
697						),
698
699				// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
700				'mpeg' => array(
701							'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
702							'group'     => 'audio-video',
703							'module'    => 'mpeg',
704							'mime_type' => 'video/mpeg',
705						),
706
707				// NSV  - audio/video - Nullsoft Streaming Video (NSV)
708				'nsv'  => array(
709							'pattern'   => '^NSV[sf]',
710							'group'     => 'audio-video',
711							'module'    => 'nsv',
712							'mime_type' => 'application/octet-stream',
713						),
714
715				// Ogg  - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
716				'ogg'  => array(
717							'pattern'   => '^OggS',
718							'group'     => 'audio',
719							'module'    => 'ogg',
720							'mime_type' => 'application/ogg',
721							'fail_id3'  => 'WARNING',
722							'fail_ape'  => 'WARNING',
723						),
724
725				// QT   - audio/video - Quicktime
726				'quicktime' => array(
727							'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
728							'group'     => 'audio-video',
729							'module'    => 'quicktime',
730							'mime_type' => 'video/quicktime',
731						),
732
733				// RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
734				'riff' => array(
735							'pattern'   => '^(RIFF|SDSS|FORM)',
736							'group'     => 'audio-video',
737							'module'    => 'riff',
738							'mime_type' => 'audio/x-wave',
739							'fail_ape'  => 'WARNING',
740						),
741
742				// Real - audio/video - RealAudio, RealVideo
743				'real' => array(
744							'pattern'   => '^(\.RMF|.ra)',
745							'group'     => 'audio-video',
746							'module'    => 'real',
747							'mime_type' => 'audio/x-realaudio',
748						),
749
750				// SWF - audio/video - ShockWave Flash
751				'swf' => array(
752							'pattern'   => '^(F|C)WS',
753							'group'     => 'audio-video',
754							'module'    => 'swf',
755							'mime_type' => 'application/x-shockwave-flash',
756						),
757
758
759				// Still-Image formats
760
761				// BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
762				'bmp'  => array(
763							'pattern'   => '^BM',
764							'group'     => 'graphic',
765							'module'    => 'bmp',
766							'mime_type' => 'image/bmp',
767							'fail_id3'  => 'ERROR',
768							'fail_ape'  => 'ERROR',
769						),
770
771				// GIF  - still image - Graphics Interchange Format
772				'gif'  => array(
773							'pattern'   => '^GIF',
774							'group'     => 'graphic',
775							'module'    => 'gif',
776							'mime_type' => 'image/gif',
777							'fail_id3'  => 'ERROR',
778							'fail_ape'  => 'ERROR',
779						),
780
781				// JPEG - still image - Joint Photographic Experts Group (JPEG)
782				'jpg'  => array(
783							'pattern'   => '^\xFF\xD8\xFF',
784							'group'     => 'graphic',
785							'module'    => 'jpg',
786							'mime_type' => 'image/jpeg',
787							'fail_id3'  => 'ERROR',
788							'fail_ape'  => 'ERROR',
789						),
790
791				// PCD  - still image - Kodak Photo CD
792				'pcd'  => array(
793							'pattern'   => '^.{2048}PCD_IPI\x00',
794							'group'     => 'graphic',
795							'module'    => 'pcd',
796							'mime_type' => 'image/x-photo-cd',
797							'fail_id3'  => 'ERROR',
798							'fail_ape'  => 'ERROR',
799						),
800
801
802				// PNG  - still image - Portable Network Graphics (PNG)
803				'png'  => array(
804							'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
805							'group'     => 'graphic',
806							'module'    => 'png',
807							'mime_type' => 'image/png',
808							'fail_id3'  => 'ERROR',
809							'fail_ape'  => 'ERROR',
810						),
811
812
813                // SVG  - still image - Scalable Vector Graphics (SVG)
814				'svg'  => array(
815							'pattern'   => '<!DOCTYPE svg PUBLIC ',
816							'group'     => 'graphic',
817							'module'    => 'svg',
818							'mime_type' => 'image/svg+xml',
819							'fail_id3'  => 'ERROR',
820							'fail_ape'  => 'ERROR',
821						),
822
823
824				// TIFF  - still image - Tagged Information File Format (TIFF)
825				'tiff' => array(
826							'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
827							'group'     => 'graphic',
828							'module'    => 'tiff',
829							'mime_type' => 'image/tiff',
830							'fail_id3'  => 'ERROR',
831							'fail_ape'  => 'ERROR',
832						),
833
834
835				// Data formats
836
837				// ISO  - data        - International Standards Organization (ISO) CD-ROM Image
838				'iso'  => array(
839							'pattern'   => '^.{32769}CD001',
840							'group'     => 'misc',
841							'module'    => 'iso',
842							'mime_type' => 'application/octet-stream',
843							'fail_id3'  => 'ERROR',
844							'fail_ape'  => 'ERROR',
845							'iconv_req' => false,
846						),
847
848				// RAR  - data        - RAR compressed data
849				'rar'  => array(
850							'pattern'   => '^Rar\!',
851							'group'     => 'archive',
852							'module'    => 'rar',
853							'mime_type' => 'application/octet-stream',
854							'fail_id3'  => 'ERROR',
855							'fail_ape'  => 'ERROR',
856						),
857
858				// SZIP - audio/data  - SZIP compressed data
859				'szip' => array(
860							'pattern'   => '^SZ\x0A\x04',
861							'group'     => 'archive',
862							'module'    => 'szip',
863							'mime_type' => 'application/octet-stream',
864							'fail_id3'  => 'ERROR',
865							'fail_ape'  => 'ERROR',
866						),
867
868				// TAR  - data        - TAR compressed data
869				'tar'  => array(
870							'pattern'   => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
871							'group'     => 'archive',
872							'module'    => 'tar',
873							'mime_type' => 'application/x-tar',
874							'fail_id3'  => 'ERROR',
875							'fail_ape'  => 'ERROR',
876						),
877
878				// GZIP  - data        - GZIP compressed data
879				'gz'  => array(
880							'pattern'   => '^\x1F\x8B\x08',
881							'group'     => 'archive',
882							'module'    => 'gzip',
883							'mime_type' => 'application/x-gzip',
884							'fail_id3'  => 'ERROR',
885							'fail_ape'  => 'ERROR',
886						),
887
888				// ZIP  - data         - ZIP compressed data
889				'zip'  => array(
890							'pattern'   => '^PK\x03\x04',
891							'group'     => 'archive',
892							'module'    => 'zip',
893							'mime_type' => 'application/zip',
894							'fail_id3'  => 'ERROR',
895							'fail_ape'  => 'ERROR',
896						),
897
898
899				// Misc other formats
900
901                // PAR2 - data        - Parity Volume Set Specification 2.0
902                'par2' => array (
903                			'pattern'   => '^PAR2\x00PKT',
904                			'group'     => 'misc',
905							'module'    => 'par2',
906							'mime_type' => 'application/octet-stream',
907							'fail_id3'  => 'ERROR',
908							'fail_ape'  => 'ERROR',
909						),
910
911				// PDF  - data        - Portable Document Format
912				'pdf'  => array(
913							'pattern'   => '^\x25PDF',
914							'group'     => 'misc',
915							'module'    => 'pdf',
916							'mime_type' => 'application/pdf',
917							'fail_id3'  => 'ERROR',
918							'fail_ape'  => 'ERROR',
919						),
920
921				// MSOFFICE  - data   - ZIP compressed data
922				'msoffice' => array(
923							'pattern'   => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
924							'group'     => 'misc',
925							'module'    => 'msoffice',
926							'mime_type' => 'application/octet-stream',
927							'fail_id3'  => 'ERROR',
928							'fail_ape'  => 'ERROR',
929						),
930			);
931		}
932
933		return $format_info;
934	}
935
936
937
938	function GetFileFormat(&$filedata, $filename='') {
939		// this function will determine the format of a file based on usually
940		// the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
941		// and in the case of ISO CD image, 6 bytes offset 32kb from the start
942		// of the file).
943
944		// Identify file format - loop through $format_info and detect with reg expr
945		foreach ($this->GetFileFormatArray() as $format_name => $info) {
946			// Using preg_match() instead of ereg() - much faster
947			// The /s switch on preg_match() forces preg_match() NOT to treat
948			// newline (0x0A) characters as special chars but do a binary match
949			if (preg_match('/'.$info['pattern'].'/s', $filedata)) {
950				$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
951				return $info;
952			}
953		}
954
955
956		if (preg_match('/\.mp[123a]$/i', $filename)) {
957			// Too many mp3 encoders on the market put gabage in front of mpeg files
958			// use assume format on these if format detection failed
959			$GetFileFormatArray = $this->GetFileFormatArray();
960			$info = $GetFileFormatArray['mp3'];
961			$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
962			return $info;
963		}
964
965		return false;
966	}
967
968
969	// converts array to $encoding charset from $this->encoding
970	function CharConvert(&$array, $encoding) {
971
972		// identical encoding - end here
973		if ($encoding == $this->encoding) {
974			return;
975		}
976
977		// loop thru array
978		foreach ($array as $key => $value) {
979
980			// go recursive
981			if (is_array($value)) {
982				$this->CharConvert($array[$key], $encoding);
983			}
984
985			// convert string
986			elseif (is_string($value)) {
987				$array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
988			}
989		}
990	}
991
992
993	function HandleAllTags() {
994
995		// key name => array (tag name, character encoding)
996		static $tags;
997		if (empty($tags)) {
998			$tags = array(
999				'asf'       => array('asf'           , 'UTF-16LE'),
1000				'midi'      => array('midi'          , 'ISO-8859-1'),
1001				'nsv'       => array('nsv'           , 'ISO-8859-1'),
1002				'ogg'       => array('vorbiscomment' , 'UTF-8'),
1003				'png'       => array('png'           , 'UTF-8'),
1004				'tiff'      => array('tiff'          , 'ISO-8859-1'),
1005				'quicktime' => array('quicktime'     , 'ISO-8859-1'),
1006				'real'      => array('real'          , 'ISO-8859-1'),
1007				'vqf'       => array('vqf'           , 'ISO-8859-1'),
1008				'zip'       => array('zip'           , 'ISO-8859-1'),
1009				'riff'      => array('riff'          , 'ISO-8859-1'),
1010				'lyrics3'   => array('lyrics3'       , 'ISO-8859-1'),
1011				'id3v1'     => array('id3v1'         , $this->encoding_id3v1),
1012				'id3v2'     => array('id3v2'         , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
1013				'ape'       => array('ape'           , 'UTF-8')
1014			);
1015		}
1016
1017		// loop thru comments array
1018		foreach ($tags as $comment_name => $tagname_encoding_array) {
1019			list($tag_name, $encoding) = $tagname_encoding_array;
1020
1021			// fill in default encoding type if not already present
1022			if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1023				$this->info[$comment_name]['encoding'] = $encoding;
1024			}
1025
1026			// copy comments if key name set
1027			if (!empty($this->info[$comment_name]['comments'])) {
1028
1029				foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1030					foreach ($valuearray as $key => $value) {
1031						if (strlen(trim($value)) > 0) {
1032							$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
1033						}
1034					}
1035				}
1036
1037				if (!isset($this->info['tags'][$tag_name])) {
1038					// comments are set but contain nothing but empty strings, so skip
1039					continue;
1040				}
1041
1042				if ($this->option_tags_html) {
1043					foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1044						foreach ($valuearray as $key => $value) {
1045							if (is_string($value)) {
1046								//$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
1047								$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
1048							} else {
1049								$this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
1050							}
1051						}
1052					}
1053				}
1054
1055				$this->CharConvert($this->info['tags'][$tag_name], $encoding);           // only copy gets converted!
1056			}
1057
1058		}
1059		return true;
1060	}
1061
1062
1063	function getHashdata($algorithm) {
1064		switch ($algorithm) {
1065			case 'md5':
1066			case 'sha1':
1067				break;
1068
1069			default:
1070				return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1071				break;
1072		}
1073
1074		if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) {
1075
1076			// We cannot get an identical md5_data value for Ogg files where the comments
1077			// span more than 1 Ogg page (compared to the same audio data with smaller
1078			// comments) using the normal getID3() method of MD5'ing the data between the
1079			// end of the comments and the end of the file (minus any trailing tags),
1080			// because the page sequence numbers of the pages that the audio data is on
1081			// do not match. Under normal circumstances, where comments are smaller than
1082			// the nominal 4-8kB page size, then this is not a problem, but if there are
1083			// very large comments, the only way around it is to strip off the comment
1084			// tags with vorbiscomment and MD5 that file.
1085			// This procedure must be applied to ALL Ogg files, not just the ones with
1086			// comments larger than 1 page, because the below method simply MD5's the
1087			// whole file with the comments stripped, not just the portion after the
1088			// comments block (which is the standard getID3() method.
1089
1090			// The above-mentioned problem of comments spanning multiple pages and changing
1091			// page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1092			// currently vorbiscomment only works on OggVorbis files.
1093
1094			if ((bool) ini_get('safe_mode')) {
1095
1096				$this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)';
1097				$this->info[$algorithm.'_data']  = false;
1098
1099			} else {
1100
1101				// Prevent user from aborting script
1102				$old_abort = ignore_user_abort(true);
1103
1104				// Create empty file
1105				$empty = tempnam('*', 'getID3');
1106				touch($empty);
1107
1108
1109				// Use vorbiscomment to make temp file without comments
1110				$temp = tempnam('*', 'getID3');
1111				$file = $this->info['filenamepath'];
1112
1113				if (GETID3_OS_ISWINDOWS) {
1114
1115					if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1116
1117						$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1118						$VorbisCommentError = `$commandline`;
1119
1120					} else {
1121
1122						$VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1123
1124					}
1125
1126				} else {
1127
1128					$commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1129					$commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1130					$VorbisCommentError = `$commandline`;
1131
1132				}
1133
1134				if (!empty($VorbisCommentError)) {
1135
1136					$this->info['warning'][]         = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
1137					$this->info[$algorithm.'_data']  = false;
1138
1139				} else {
1140
1141					// Get hash of newly created file
1142					switch ($algorithm) {
1143						case 'md5':
1144							$this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp);
1145							break;
1146
1147						case 'sha1':
1148							$this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp);
1149							break;
1150					}
1151				}
1152
1153				// Clean up
1154				unlink($empty);
1155				unlink($temp);
1156
1157				// Reset abort setting
1158				ignore_user_abort($old_abort);
1159
1160			}
1161
1162		} else {
1163
1164			if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1165
1166				// get hash from part of file
1167				$this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1168
1169			} else {
1170
1171				// get hash from whole file
1172				switch ($algorithm) {
1173					case 'md5':
1174						$this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']);
1175						break;
1176
1177					case 'sha1':
1178						$this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']);
1179						break;
1180				}
1181			}
1182
1183		}
1184		return true;
1185	}
1186
1187
1188	function ChannelsBitratePlaytimeCalculations() {
1189
1190		// set channelmode on audio
1191		if (@$this->info['audio']['channels'] == '1') {
1192			$this->info['audio']['channelmode'] = 'mono';
1193		} elseif (@$this->info['audio']['channels'] == '2') {
1194			$this->info['audio']['channelmode'] = 'stereo';
1195		}
1196
1197		// Calculate combined bitrate - audio + video
1198		$CombinedBitrate  = 0;
1199		$CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1200		$CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1201		if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1202			$this->info['bitrate'] = $CombinedBitrate;
1203		}
1204		//if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1205		//	// for example, VBR MPEG video files cannot determine video bitrate:
1206		//	// should not set overall bitrate and playtime from audio bitrate only
1207		//	unset($this->info['bitrate']);
1208		//}
1209
1210		// video bitrate undetermined, but calculable
1211		if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1212			// if video bitrate not set
1213			if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1214				// AND if audio bitrate is set to same as overall bitrate
1215				if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1216					// AND if playtime is set
1217					if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1218						// AND if AV data offset start/end is known
1219						// THEN we can calculate the video bitrate
1220						$this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1221						$this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1222					}
1223				}
1224			}
1225		}
1226
1227		if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) {
1228			$this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1229		}
1230
1231		// Set playtime string
1232		if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1233			$this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1234		}
1235	}
1236
1237
1238	function CalculateCompressionRatioVideo() {
1239		if (empty($this->info['video'])) {
1240			return false;
1241		}
1242		if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1243			return false;
1244		}
1245		if (empty($this->info['video']['bits_per_sample'])) {
1246			return false;
1247		}
1248
1249		switch ($this->info['video']['dataformat']) {
1250			case 'bmp':
1251			case 'gif':
1252			case 'jpeg':
1253			case 'jpg':
1254			case 'png':
1255			case 'tiff':
1256				$FrameRate = 1;
1257				$PlaytimeSeconds = 1;
1258				$BitrateCompressed = $this->info['filesize'] * 8;
1259				break;
1260
1261			default:
1262				if (!empty($this->info['video']['frame_rate'])) {
1263					$FrameRate = $this->info['video']['frame_rate'];
1264				} else {
1265					return false;
1266				}
1267				if (!empty($this->info['playtime_seconds'])) {
1268					$PlaytimeSeconds = $this->info['playtime_seconds'];
1269				} else {
1270					return false;
1271				}
1272				if (!empty($this->info['video']['bitrate'])) {
1273					$BitrateCompressed = $this->info['video']['bitrate'];
1274				} else {
1275					return false;
1276				}
1277				break;
1278		}
1279		$BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1280
1281		$this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1282		return true;
1283	}
1284
1285
1286	function CalculateCompressionRatioAudio() {
1287		if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
1288			return false;
1289		}
1290		$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));
1291
1292		if (!empty($this->info['audio']['streams'])) {
1293			foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1294				if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1295					$this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1296				}
1297			}
1298		}
1299		return true;
1300	}
1301
1302
1303	function CalculateReplayGain() {
1304		if (isset($this->info['replay_gain'])) {
1305			$this->info['replay_gain']['reference_volume'] = 89;
1306			if (isset($this->info['replay_gain']['track']['adjustment'])) {
1307				$this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1308			}
1309			if (isset($this->info['replay_gain']['album']['adjustment'])) {
1310				$this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1311			}
1312
1313			if (isset($this->info['replay_gain']['track']['peak'])) {
1314				$this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1315			}
1316			if (isset($this->info['replay_gain']['album']['peak'])) {
1317				$this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1318			}
1319		}
1320		return true;
1321	}
1322
1323	function ProcessAudioStreams() {
1324		if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1325			if (!isset($this->info['audio']['streams'])) {
1326				foreach ($this->info['audio'] as $key => $value) {
1327					if ($key != 'streams') {
1328						$this->info['audio']['streams'][0][$key] = $value;
1329					}
1330				}
1331			}
1332		}
1333		return true;
1334	}
1335
1336	function getid3_tempnam() {
1337		return tempnam($this->tempdir, 'gI3');
1338	}
1339
1340}
1341
1342?>