27 | | //// Settings Section - do NOT modify this file - change setting after newing getid3! |
28 | | |
29 | | // Encoding |
30 | | public $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv() - see http://www.gnu.org/software/libiconv/). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE. |
31 | | public $encoding_id3v1 = 'ISO-8859-1'; // Override SPECIFICATION encoding for broken ID3v1 tags caused by bad tag programs. Examples: 'EUC-CN' for "Chinese MP3s" and 'CP1251' for "Cyrillic". |
32 | | public $encoding_id3v2 = 'ISO-8859-1'; // Override ISO-8859-1 encoding for broken ID3v2 tags caused by BRAINDEAD tag programs that writes system codepage as 'ISO-8859-1' instead of UTF-8. |
33 | | |
34 | | // Tags - disable for speed |
35 | | public $option_tag_id3v1 = true; // Read and process ID3v1 tags. |
36 | | public $option_tag_id3v2 = true; // Read and process ID3v2 tags. |
37 | | public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags. |
38 | | public $option_tag_apetag = true; // Read and process APE tags. |
39 | | |
40 | | // Misc calucations - disable for speed |
41 | | public $option_analyze = true; // Analyze file - disable if you only need to detect file format. |
42 | | public $option_accurate_results = true; // Disable to greatly speed up parsing of some file formats at the cost of accuracy. |
43 | | public $option_tags_process = true; // Copy tags to root key 'tags' and 'comments' and encode to $this->encoding. |
44 | | public $option_tags_images = false; // Scan tags for binary image data - ID3v2 and vorbiscomments only. |
45 | | public $option_extra_info = true; // Calculate/return additional info such as bitrate, channelmode etc. |
46 | | public $option_max_2gb_check = false; // Check whether file is larger than 2 Gb and thus not supported by PHP. |
47 | | |
48 | | // Misc data hashes - slow - require hash module |
49 | | public $option_md5_data = false; // Get MD5 sum of data part - slow. |
50 | | public $option_md5_data_source = false; // Use MD5 of source file if available - only FLAC, MAC, OptimFROG and Wavpack4. |
51 | | public $option_sha1_data = false; // Get SHA1 sum of data part - slow. |
52 | | |
53 | | // Public variables |
54 | | public $filename; // Filename of file being analysed. |
55 | | public $fp; // Filepointer to file being analysed. |
56 | | public $info; // Result array. |
57 | | |
58 | | // Protected variables |
59 | | protected $include_path; // getid3 include path. |
60 | | protected $warnings = array (); |
61 | | protected $iconv_present; |
62 | | |
63 | | // Class constants |
64 | | const VERSION = '2.0.0b4'; |
65 | | const FREAD_BUFFER_SIZE = 16384; // Read buffer size in bytes. |
66 | | const ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ |
67 | | ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'; |
68 | | |
69 | | |
70 | | |
71 | | // Constructor - check PHP enviroment and load library. |
72 | | public function __construct() { |
73 | | |
74 | | // Static varibles - no need to recalc every time we new getid3. |
75 | | static $include_path; |
76 | | static $iconv_present; |
77 | | |
78 | | |
79 | | static $initialized; |
80 | | if ($initialized) { |
81 | | |
82 | | // Import static variables |
83 | | $this->include_path = $include_path; |
84 | | $this->iconv_present = $iconv_present; |
85 | | |
86 | | // Run init checks only on first instance. |
87 | | return; |
88 | | } |
89 | | |
90 | | // Get include_path |
91 | | $this->include_path = $include_path = dirname(__FILE__) . '/'; |
92 | | |
93 | | // Check for presence of iconv() and make sure it works (simpel test only). |
94 | | if (function_exists('iconv') && @iconv('UTF-16LE', 'ISO-8859-1', @iconv('ISO-8859-1', 'UTF-16LE', getid3::ICONV_TEST_STRING)) == getid3::ICONV_TEST_STRING) { |
95 | | $this->iconv_present = $iconv_present = true; |
96 | | } |
97 | | |
98 | | // iconv() not present - load replacement module. |
99 | | else { |
100 | | $this->include_module('lib.iconv_replacement'); |
101 | | $this->iconv_present = $iconv_present = false; |
102 | | } |
103 | | |
104 | | |
105 | | // Require magic_quotes_runtime off |
106 | | if (get_magic_quotes_runtime()) { |
107 | | throw new getid3_exception('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'); |
108 | | } |
109 | | |
110 | | |
111 | | // Check memory limit. |
112 | | $memory_limit = ini_get('memory_limit'); |
113 | | if (eregi('([0-9]+)M', $memory_limit, $matches)) { |
114 | | // could be stored as "16M" rather than 16777216 for example |
115 | | $memory_limit = $matches[1] * 1048576; |
116 | | } |
117 | | if ($memory_limit <= 0) { |
118 | | // Should not happen. |
119 | | } elseif ($memory_limit <= 4194304) { |
120 | | $this->warning('[SERIOUS] PHP has less than 4 Mb available memory and will very likely run out. Increase memory_limit in php.ini.'); |
121 | | } elseif ($memory_limit <= 12582912) { |
122 | | $this->warning('PHP has less than 12 Mb available memory and might run out if all modules are loaded. Increase memory_limit in php.ini if needed.'); |
123 | | } |
124 | | |
125 | | |
126 | | // Check safe_mode off |
127 | | if ((bool)ini_get('safe_mode')) { |
128 | | $this->warning('Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbis/flac tag writing disabled.'); |
129 | | } |
130 | | |
131 | | $initialized = true; |
132 | | } |
133 | | |
134 | | |
135 | | |
136 | | // Analyze file by name |
137 | | public function Analyze($filename) { |
138 | | |
139 | | // Init and save values |
140 | | $this->filename = $filename; |
141 | | $this->warnings = array (); |
142 | | |
143 | | // Init result array and set parameters |
144 | | $this->info = array (); |
145 | | $this->info['GETID3_VERSION'] = getid3::VERSION; |
146 | | |
147 | | // Remote files not supported |
148 | | if (preg_match('/^(ht|f)tp:\/\//', $filename)) { |
149 | | throw new getid3_exception('Remote files are not supported - please copy the file locally first.'); |
150 | | } |
151 | | |
152 | | // Open local file |
153 | | if (!$this->fp = @fopen($filename, 'rb')) { |
154 | | throw new getid3_exception('Could not open file "'.$filename.'"'); |
155 | | } |
156 | | |
157 | | // Set filesize related parameters |
158 | | $this->info['filesize'] = filesize($filename); |
159 | | $this->info['avdataoffset'] = 0; |
160 | | $this->info['avdataend'] = $this->info['filesize']; |
161 | | |
162 | | // Option_max_2gb_check |
163 | | if ($this->option_max_2gb_check) { |
164 | | // PHP doesn't support integers larger than 31-bit (~2GB) |
165 | | // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize |
166 | | // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer |
167 | | fseek($this->fp, 0, SEEK_END); |
168 | | if ((($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || |
169 | | ($this->info['filesize'] < 0) || |
170 | | (ftell($this->fp) < 0)) { |
171 | | unset($this->info['filesize']); |
172 | | fclose($this->fp); |
173 | | throw new getid3_exception('File is most likely larger than 2GB and is not supported by PHP.'); |
174 | | } |
175 | | } |
176 | | |
177 | | |
178 | | // ID3v2 detection (NOT parsing) done to make fileformat easier. |
179 | | if (!$this->option_tag_id3v2) { |
180 | | |
181 | | fseek($this->fp, 0, SEEK_SET); |
182 | | $header = fread($this->fp, 10); |
183 | | if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { |
184 | | $this->info['id3v2']['header'] = true; |
185 | | $this->info['id3v2']['majorversion'] = ord($header{3}); |
186 | | $this->info['id3v2']['minorversion'] = ord($header{4}); |
187 | | $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length |
188 | | } |
189 | | } |
190 | | |
191 | | |
192 | | // Handle tags |
193 | | foreach (array ("id3v2", "id3v1", "apetag", "lyrics3") as $tag_name) { |
194 | | |
195 | | $option_tag = 'option_tag_' . $tag_name; |
196 | | if ($this->$option_tag) { |
197 | | $this->include_module('tag.'.$tag_name); |
198 | | try { |
199 | | $tag_class = 'getid3_' . $tag_name; |
200 | | $tag = new $tag_class($this); |
201 | | $tag->Analyze(); |
202 | | } |
203 | | catch (getid3_exception $e) { |
204 | | throw $e; |
205 | | } |
206 | | } |
207 | | } |
208 | | |
209 | | |
210 | | |
211 | | //// Determine file format by magic bytes in file header. |
212 | | |
213 | | // Read 32 kb file data |
214 | | fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); |
215 | | $filedata = fread($this->fp, 32774); |
216 | | |
217 | | // Get huge FileFormatArray |
218 | | $file_format_array = getid3::GetFileFormatArray(); |
219 | | |
220 | | // Identify file format - loop through $format_info and detect with reg expr |
221 | | foreach ($file_format_array as $name => $info) { |
222 | | |
223 | | if (preg_match('/'.$info['pattern'].'/s', $filedata)) { // The /s switch on preg_match() forces preg_match() NOT to treat newline (0x0A) characters as special chars but do a binary match |
224 | | |
225 | | // Format detected but not supported |
226 | | if (!@$info['module'] || !@$info['group']) { |
227 | | fclose($this->fp); |
228 | | $this->info['fileformat'] = $name; |
229 | | $this->info['mime_type'] = $info['mime_type']; |
230 | | $this->warning('Format only detected. Parsing not available yet.'); |
231 | | $this->info['warning'] = $this->warnings; |
232 | | return $this->info; |
233 | | } |
234 | | |
235 | | $determined_format = $info; // copy $info deleted by foreach() |
236 | | continue; |
237 | | } |
238 | | } |
239 | | |
240 | | // Unable to determine file format |
241 | | if (!@$determined_format) { |
242 | | |
243 | | // Too many mp3 encoders on the market put gabage in front of mpeg files |
244 | | // use assume format on these if format detection failed |
245 | | if (preg_match('/\.mp[123a]$/i', $filename)) { |
246 | | $determined_format = $file_format_array['mp3']; |
247 | | } |
248 | | |
249 | | else { |
250 | | fclose($this->fp); |
251 | | throw new getid3_exception('Unable to determine file format'); |
252 | | } |
253 | | } |
254 | | |
255 | | // Free memory |
256 | | unset($file_format_array); |
257 | | |
258 | | // Check for illegal ID3 tags |
259 | | if (@$determined_format['fail_id3'] && (@$this->info['id3v1'] || @$this->info['id3v2'])) { |
260 | | if ($determined_format['fail_id3'] === 'ERROR') { |
261 | | fclose($this->fp); |
262 | | throw new getid3_exception('ID3 tags not allowed on this file type.'); |
263 | | } |
264 | | elseif ($determined_format['fail_id3'] === 'WARNING') { |
265 | | @$this->info['id3v1'] and $this->warning('ID3v1 tags not allowed on this file type.'); |
266 | | @$this->info['id3v2'] and $this->warning('ID3v2 tags not allowed on this file type.'); |
267 | | } |
268 | | } |
269 | | |
270 | | // Check for illegal APE tags |
271 | | if (@$determined_format['fail_ape'] && @$this->info['tags']['ape']) { |
272 | | if ($determined_format['fail_ape'] === 'ERROR') { |
273 | | fclose($this->fp); |
274 | | throw new getid3_exception('APE tags not allowed on this file type.'); |
275 | | } elseif ($determined_format['fail_ape'] === 'WARNING') { |
276 | | $this->warning('APE tags not allowed on this file type.'); |
277 | | } |
278 | | } |
279 | | |
280 | | |
281 | | // Set mime type |
282 | | $this->info['mime_type'] = $determined_format['mime_type']; |
283 | | |
284 | | // Calc module file name |
285 | | $determined_format['include'] = 'module.'.$determined_format['group'].'.'.$determined_format['module'].'.php'; |
286 | | |
287 | | // Supported format signature pattern detected, but module deleted. |
288 | | if (!file_exists($this->include_path.$determined_format['include'])) { |
289 | | fclose($this->fp); |
290 | | throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', was removed.'); |
291 | | } |
292 | | |
293 | | // Include module |
294 | | $this->include_module($determined_format['group'].'.'.$determined_format['module']); |
295 | | |
296 | | // Instantiate module class and analyze |
297 | | $class_name = 'getid3_'.$determined_format['module']; |
298 | | if (!class_exists($class_name)) { |
299 | | throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', is corrupt.'); |
300 | | } |
301 | | $class = new $class_name($this); |
302 | | |
303 | | try { |
304 | | $this->option_analyze and $class->Analyze(); |
305 | | } |
306 | | catch (getid3_exception $e) { |
307 | | throw $e; |
308 | | } |
309 | | catch (Exception $e) { |
310 | | throw new getid3_exception('Corrupt file.'); |
311 | | } |
312 | | |
313 | | // Close file |
314 | | fclose($this->fp); |
315 | | |
316 | | // Optional - Process all tags - copy to 'tags' and convert charsets |
317 | | if ($this->option_tags_process) { |
318 | | $this->HandleAllTags(); |
319 | | } |
320 | | |
321 | | |
322 | | //// Optional - perform more calculations |
323 | | if ($this->option_extra_info) { |
324 | | |
325 | | // Set channelmode on audio |
326 | | if (@$this->info['audio']['channels'] == '1') { |
327 | | $this->info['audio']['channelmode'] = 'mono'; |
328 | | } elseif (@$this->info['audio']['channels'] == '2') { |
329 | | $this->info['audio']['channelmode'] = 'stereo'; |
330 | | } |
331 | | |
332 | | // Calculate combined bitrate - audio + video |
333 | | $combined_bitrate = 0; |
334 | | $combined_bitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); |
335 | | $combined_bitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); |
336 | | if (($combined_bitrate > 0) && empty($this->info['bitrate'])) { |
337 | | $this->info['bitrate'] = $combined_bitrate; |
338 | | } |
339 | | if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) { |
340 | | $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; |
341 | | } |
342 | | |
343 | | // Set playtime string |
344 | | if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { |
345 | | $this->info['playtime_string'] = floor(round($this->info['playtime_seconds']) / 60) . ':' . str_pad(floor(round($this->info['playtime_seconds']) % 60), 2, 0, STR_PAD_LEFT);; |
346 | | } |
347 | | |
348 | | |
349 | | // CalculateCompressionRatioVideo() { |
350 | | if (@$this->info['video'] && @$this->info['video']['resolution_x'] && @$this->info['video']['resolution_y'] && @$this->info['video']['bits_per_sample']) { |
351 | | |
352 | | // From static image formats |
353 | | if (in_array($this->info['video']['dataformat'], array ('bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff'))) { |
354 | | $frame_rate = 1; |
355 | | $bitrate_compressed = $this->info['filesize'] * 8; |
356 | | } |
357 | | |
358 | | // From video formats |
359 | | else { |
360 | | $frame_rate = @$this->info['video']['frame_rate']; |
361 | | $bitrate_compressed = @$this->info['video']['bitrate']; |
362 | | } |
363 | | |
364 | | if ($frame_rate && $bitrate_compressed) { |
365 | | $this->info['video']['compression_ratio'] = $bitrate_compressed / ($this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $frame_rate); |
366 | | } |
367 | | } |
368 | | |
369 | | |
370 | | // CalculateCompressionRatioAudio() { |
371 | | if (@$this->info['audio']['bitrate'] && @$this->info['audio']['channels'] && @$this->info['audio']['sample_rate']) { |
372 | | $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (@$this->info['audio']['bits_per_sample'] ? $this->info['audio']['bits_per_sample'] : 16)); |
373 | | } |
374 | | |
375 | | if (@$this->info['audio']['streams']) { |
376 | | foreach ($this->info['audio']['streams'] as $stream_number => $stream_data) { |
377 | | if (@$stream_data['bitrate'] && @$stream_data['channels'] && @$stream_data['sample_rate']) { |
378 | | $this->info['audio']['streams'][$stream_number]['compression_ratio'] = $stream_data['bitrate'] / ($stream_data['channels'] * $stream_data['sample_rate'] * (@$stream_data['bits_per_sample'] ? $stream_data['bits_per_sample'] : 16)); |
379 | | } |
380 | | } |
381 | | } |
382 | | |
383 | | |
384 | | // CalculateReplayGain() { |
385 | | if (@$this->info['replay_gain']) { |
386 | | if (!@$this->info['replay_gain']['reference_volume']) { |
387 | | $this->info['replay_gain']['reference_volume'] = 89; |
388 | | } |
389 | | if (isset($this->info['replay_gain']['track']['adjustment'])) { |
390 | | $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; |
391 | | } |
392 | | if (isset($this->info['replay_gain']['album']['adjustment'])) { |
393 | | $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; |
394 | | } |
395 | | |
396 | | if (isset($this->info['replay_gain']['track']['peak'])) { |
397 | | $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['track']['peak']); |
398 | | } |
399 | | if (isset($this->info['replay_gain']['album']['peak'])) { |
400 | | $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['album']['peak']); |
401 | | } |
402 | | } |
403 | | |
404 | | |
405 | | // ProcessAudioStreams() { |
406 | | if (@!$this->info['audio']['streams'] && (@$this->info['audio']['bitrate'] || @$this->info['audio']['channels'] || @$this->info['audio']['sample_rate'])) { |
407 | | foreach ($this->info['audio'] as $key => $value) { |
408 | | if ($key != 'streams') { |
409 | | $this->info['audio']['streams'][0][$key] = $value; |
410 | | } |
411 | | } |
412 | | } |
413 | | } |
414 | | |
415 | | |
416 | | // Get the md5/sha1sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags. |
417 | | if ($this->option_md5_data || $this->option_sha1_data) { |
418 | | |
419 | | // Load data-hash library if needed |
420 | | $this->include_module('lib.data_hash'); |
421 | | |
422 | | if ($this->option_sha1_data) { |
423 | | new getid3_lib_data_hash($this, 'sha1'); |
424 | | } |
425 | | |
426 | | if ($this->option_md5_data) { |
427 | | |
428 | | // no md5_data_source or option disabled -- md5_data_source supported by FLAC, MAC, OptimFROG, Wavpack4 |
429 | | if (!$this->option_md5_data_source || !@$this->info['md5_data_source']) { |
430 | | new getid3_lib_data_hash($this, 'md5'); |
431 | | } |
432 | | |
433 | | // copy md5_data_source to md5_data if option set to true |
434 | | elseif ($this->option_md5_data_source && @$this->info['md5_data_source']) { |
435 | | $this->info['md5_data'] = $this->info['md5_data_source']; |
436 | | } |
437 | | } |
438 | | } |
439 | | |
440 | | // Set warnings |
441 | | if ($this->warnings) { |
442 | | $this->info['warning'] = $this->warnings; |
443 | | } |
444 | | |
445 | | // Return result |
446 | | return $this->info; |
447 | | } |
448 | | |
449 | | |
450 | | |
451 | | // Return array of warnings |
452 | | public function warnings() { |
453 | | |
454 | | return $this->warnings; |
455 | | } |
456 | | |
457 | | |
458 | | |
459 | | // Add warning(s) to $this->warnings[] |
460 | | public function warning($message) { |
461 | | |
462 | | if (is_array($message)) { |
463 | | $this->warnings = array_merge($this->warnings, $message); |
464 | | } |
465 | | else { |
466 | | $this->warnings[] = $message; |
467 | | } |
468 | | } |
469 | | |
470 | | |
471 | | |
472 | | // Clear all warnings when cloning |
473 | | public function __clone() { |
474 | | |
475 | | $this->warnings = array (); |
476 | | |
477 | | // Copy info array, otherwise it will be a reference. |
478 | | $temp = $this->info; |
479 | | unset($this->info); |
480 | | $this->info = $temp; |
481 | | } |
482 | | |
483 | | |
484 | | |
485 | | // Convert string between charsets -- iconv() wrapper |
486 | | public function iconv($in_charset, $out_charset, $string, $drop01 = false) { |
487 | | |
488 | | if ($drop01 && ($string === "\x00" || $string === "\x01")) { |
489 | | return ''; |
490 | | } |
491 | | |
492 | | |
493 | | if (!$this->iconv_present) { |
494 | | return getid3_iconv_replacement::iconv($in_charset, $out_charset, $string); |
495 | | } |
496 | | |
497 | | |
498 | | // iconv() present |
499 | | if ($result = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { |
500 | | |
501 | | if ($out_charset == 'ISO-8859-1') { |
502 | | return rtrim($result, "\x00"); |
503 | | } |
504 | | return $result; |
505 | | } |
506 | | |
507 | | $this->warning('iconv() was unable to convert the string: "' . $string . '" from ' . $in_charset . ' to ' . $out_charset); |
508 | | return $string; |
509 | | } |
510 | | |
511 | | |
512 | | |
513 | | public function include_module($name) { |
514 | | |
515 | | if (!file_exists($this->include_path.'module.'.$name.'.php')) { |
516 | | throw new getid3_exception('Required module.'.$name.'.php is missing.'); |
517 | | } |
518 | | |
519 | | include_once($this->include_path.'module.'.$name.'.php'); |
520 | | } |
521 | | |
522 | | |
523 | | |
524 | | public function include_module_optional($name) { |
525 | | |
526 | | if (!file_exists($this->include_path.'module.'.$name.'.php')) { |
527 | | return; |
528 | | } |
529 | | |
530 | | include_once($this->include_path.'module.'.$name.'.php'); |
531 | | return true; |
532 | | } |
533 | | |
534 | | |
535 | | // Return array containing information about all supported formats |
536 | | public static function GetFileFormatArray() { |
537 | | |
538 | | static $format_info = array ( |
539 | | |
540 | | // Audio formats |
541 | | |
542 | | // AC-3 - audio - Dolby AC-3 / Dolby Digital |
543 | | 'ac3' => array ( |
544 | | 'pattern' => '^\x0B\x77', |
545 | | 'group' => 'audio', |
546 | | 'module' => 'ac3', |
547 | | 'mime_type' => 'audio/ac3', |
548 | | ), |
549 | | |
550 | | // AAC - audio - Advanced Audio Coding (AAC) - ADIF format |
551 | | 'adif' => array ( |
552 | | 'pattern' => '^ADIF', |
553 | | 'group' => 'audio', |
554 | | 'module' => 'aac_adif', |
555 | | 'mime_type' => 'application/octet-stream', |
556 | | 'fail_ape' => 'WARNING', |
557 | | ), |
558 | | |
559 | | |
560 | | // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) |
561 | | 'adts' => array ( |
562 | | 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', |
563 | | 'group' => 'audio', |
564 | | 'module' => 'aac_adts', |
565 | | 'mime_type' => 'application/octet-stream', |
566 | | 'fail_ape' => 'WARNING', |
567 | | ), |
568 | | |
569 | | |
570 | | // AU - audio - NeXT/Sun AUdio (AU) |
571 | | 'au' => array ( |
572 | | 'pattern' => '^\.snd', |
573 | | 'group' => 'audio', |
574 | | 'module' => 'au', |
575 | | 'mime_type' => 'audio/basic', |
576 | | ), |
577 | | |
578 | | // AVR - audio - Audio Visual Research |
579 | | 'avr' => array ( |
580 | | 'pattern' => '^2BIT', |
581 | | 'group' => 'audio', |
582 | | 'module' => 'avr', |
583 | | 'mime_type' => 'application/octet-stream', |
584 | | ), |
585 | | |
586 | | // BONK - audio - Bonk v0.9+ |
587 | | 'bonk' => array ( |
588 | | 'pattern' => '^\x00(BONK|INFO|META| ID3)', |
589 | | 'group' => 'audio', |
590 | | 'module' => 'bonk', |
591 | | 'mime_type' => 'audio/xmms-bonk', |
592 | | ), |
593 | | |
594 | | // DTS - audio - Dolby Theatre System |
| 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 | if (isset($this->$opt) === false) { |
| 146 | continue; |
| 147 | } |
| 148 | $this->$opt = $val; |
| 149 | } |
| 150 | return true; |
| 151 | } |
| 152 | |
| 153 | |
| 154 | // public: analyze file - replaces GetAllFileInfo() and GetTagOnly() |
| 155 | function analyze($filename) { |
| 156 | |
| 157 | if (!empty($this->startup_error)) { |
| 158 | return $this->error($this->startup_error); |
| 159 | } |
| 160 | if (!empty($this->startup_warning)) { |
| 161 | $this->warning($this->startup_warning); |
| 162 | } |
| 163 | |
| 164 | // init result array and set parameters |
| 165 | $this->info = array(); |
| 166 | $this->info['GETID3_VERSION'] = GETID3_VERSION; |
| 167 | |
| 168 | // Check encoding/iconv support |
| 169 | if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { |
| 170 | $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; |
| 171 | if (GETID3_OS_ISWINDOWS) { |
| 172 | $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'; |
| 173 | } else { |
| 174 | $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; |
| 175 | } |
| 176 | return $this->error($errormessage); |
| 177 | } |
| 178 | |
| 179 | // Disable magic_quotes_runtime, if neccesary |
| 180 | $old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime |
| 181 | if ($old_magic_quotes_runtime) { |
| 182 | set_magic_quotes_runtime(0); // turn off magic_quotes_runtime |
| 183 | if (get_magic_quotes_runtime()) { |
| 184 | return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled'); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // remote files not supported |
| 189 | if (preg_match('/^(ht|f)tp:\/\//', $filename)) { |
| 190 | return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first'); |
| 191 | } |
| 192 | |
| 193 | $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename); |
| 194 | $filename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#', DIRECTORY_SEPARATOR, $filename); |
| 195 | |
| 196 | // open local file |
| 197 | if (file_exists($filename) && ($fp = @fopen($filename, 'rb'))) { |
| 198 | // great |
| 199 | } else { |
| 200 | return $this->error('Could not open file "'.$filename.'"'); |
| 201 | } |
| 202 | |
| 203 | // set parameters |
| 204 | $this->info['filesize'] = filesize($filename); |
| 205 | |
| 206 | // option_max_2gb_check |
| 207 | if ($this->option_max_2gb_check) { |
| 208 | // PHP doesn't support integers larger than 31-bit (~2GB) |
| 209 | // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize |
| 210 | // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer |
| 211 | fseek($fp, 0, SEEK_END); |
| 212 | if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) || |
| 213 | ($this->info['filesize'] < 0) || |
| 214 | (ftell($fp) < 0)) { |
| 215 | $real_filesize = false; |
| 216 | if (GETID3_OS_ISWINDOWS) { |
| 217 | $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"'; |
| 218 | $dir_output = `$commandline`; |
| 219 | if (eregi('1 File\(s\)[ ]+([0-9]+) bytes', $dir_output, $matches)) { |
| 220 | $real_filesize = (float) $matches[1]; |
| 221 | } |
| 222 | } else { |
| 223 | $commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename); |
| 224 | $dir_output = `$commandline`; |
| 225 | if (eregi('([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.preg_quote($filename).'$', $dir_output, $matches)) { |
| 226 | $real_filesize = (float) $matches[1]; |
| 227 | } |
| 228 | } |
| 229 | if ($real_filesize === false) { |
| 230 | unset($this->info['filesize']); |
| 231 | fclose($fp); |
| 232 | return $this->error('File is most likely larger than 2GB and is not supported by PHP'); |
| 233 | } elseif ($real_filesize < pow(2, 31)) { |
| 234 | unset($this->info['filesize']); |
| 235 | fclose($fp); |
| 236 | return $this->error('PHP seems to think the file is larger than 2GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org'); |
| 237 | } |
| 238 | $this->info['filesize'] = $real_filesize; |
| 239 | $this->error('File is larger than 2GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.'); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | // set more parameters |
| 244 | $this->info['avdataoffset'] = 0; |
| 245 | $this->info['avdataend'] = $this->info['filesize']; |
| 246 | $this->info['fileformat'] = ''; // filled in later |
| 247 | $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used |
| 248 | $this->info['video']['dataformat'] = ''; // filled in later, unset if not used |
| 249 | $this->info['tags'] = array(); // filled in later, unset if not used |
| 250 | $this->info['error'] = array(); // filled in later, unset if not used |
| 251 | $this->info['warning'] = array(); // filled in later, unset if not used |
| 252 | $this->info['comments'] = array(); // filled in later, unset if not used |
| 253 | $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired |
| 254 | |
| 255 | // set redundant parameters - might be needed in some include file |
| 256 | $this->info['filename'] = basename($filename); |
| 257 | $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); |
| 258 | $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; |
| 259 | |
| 260 | |
| 261 | // handle ID3v2 tag - done first - already at beginning of file |
| 262 | // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect |
| 263 | if ($this->option_tag_id3v2) { |
| 264 | |
| 265 | $GETID3_ERRORARRAY = &$this->info['warning']; |
| 266 | if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) { |
| 267 | $tag = new getid3_id3v2($fp, $this->info); |
| 268 | unset($tag); |
| 269 | } |
| 270 | |
| 271 | } else { |
| 272 | |
| 273 | fseek($fp, 0, SEEK_SET); |
| 274 | $header = fread($fp, 10); |
| 275 | if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { |
| 276 | $this->info['id3v2']['header'] = true; |
| 277 | $this->info['id3v2']['majorversion'] = ord($header{3}); |
| 278 | $this->info['id3v2']['minorversion'] = ord($header{4}); |
| 279 | $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 |
| 280 | |
| 281 | $this->info['id3v2']['tag_offset_start'] = 0; |
| 282 | $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength']; |
| 283 | $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end']; |
| 284 | } |
| 285 | |
| 286 | } |
| 287 | |
| 288 | |
| 289 | // handle ID3v1 tag |
| 290 | if ($this->option_tag_id3v1) { |
| 291 | if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) { |
| 292 | return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.'); |
| 293 | } |
| 294 | $tag = new getid3_id3v1($fp, $this->info); |
| 295 | unset($tag); |
| 296 | } |
| 297 | |
| 298 | // handle APE tag |
| 299 | if ($this->option_tag_apetag) { |
| 300 | if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) { |
| 301 | return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.'); |
| 302 | } |
| 303 | $tag = new getid3_apetag($fp, $this->info); |
| 304 | unset($tag); |
| 305 | } |
| 306 | |
| 307 | // handle lyrics3 tag |
| 308 | if ($this->option_tag_lyrics3) { |
| 309 | if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) { |
| 310 | return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.'); |
| 311 | } |
| 312 | $tag = new getid3_lyrics3($fp, $this->info); |
| 313 | unset($tag); |
| 314 | } |
| 315 | |
| 316 | // read 32 kb file data |
| 317 | fseek($fp, $this->info['avdataoffset'], SEEK_SET); |
| 318 | $formattest = fread($fp, 32774); |
| 319 | |
| 320 | // determine format |
| 321 | $determined_format = $this->GetFileFormat($formattest, $filename); |
| 322 | |
| 323 | // unable to determine file format |
| 324 | if (!$determined_format) { |
| 325 | fclose($fp); |
| 326 | return $this->error('unable to determine file format'); |
| 327 | } |
| 328 | |
| 329 | // check for illegal ID3 tags |
| 330 | if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { |
| 331 | if ($determined_format['fail_id3'] === 'ERROR') { |
| 332 | fclose($fp); |
| 333 | return $this->error('ID3 tags not allowed on this file type.'); |
| 334 | } elseif ($determined_format['fail_id3'] === 'WARNING') { |
| 335 | $this->info['warning'][] = 'ID3 tags not allowed on this file type.'; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | // check for illegal APE tags |
| 340 | if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { |
| 341 | if ($determined_format['fail_ape'] === 'ERROR') { |
| 342 | fclose($fp); |
| 343 | return $this->error('APE tags not allowed on this file type.'); |
| 344 | } elseif ($determined_format['fail_ape'] === 'WARNING') { |
| 345 | $this->info['warning'][] = 'APE tags not allowed on this file type.'; |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | // set mime type |
| 350 | $this->info['mime_type'] = $determined_format['mime_type']; |
| 351 | |
| 352 | // supported format signature pattern detected, but module deleted |
| 353 | if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { |
| 354 | fclose($fp); |
| 355 | return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.'); |
| 356 | } |
| 357 | |
| 358 | // module requires iconv support |
| 359 | if (!function_exists('iconv') && @$determined_format['iconv_req']) { |
| 360 | return $this->error('iconv support is required for this module ('.$determined_format['include'].').'); |
| 361 | } |
| 362 | |
| 363 | // include module |
| 364 | include_once(GETID3_INCLUDEPATH.$determined_format['include']); |
| 365 | |
| 366 | // instantiate module class |
| 367 | $class_name = 'getid3_'.$determined_format['module']; |
| 368 | if (!class_exists($class_name)) { |
| 369 | return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.'); |
| 370 | } |
| 371 | if (isset($determined_format['option'])) { |
| 372 | $class = new $class_name($fp, $this->info, $determined_format['option']); |
| 373 | } else { |
| 374 | $class = new $class_name($fp, $this->info); |
| 375 | } |
| 376 | unset($class); |
| 377 | |
| 378 | // close file |
| 379 | fclose($fp); |
| 380 | |
| 381 | // process all tags - copy to 'tags' and convert charsets |
| 382 | if ($this->option_tags_process) { |
| 383 | $this->HandleAllTags(); |
| 384 | } |
| 385 | |
| 386 | // perform more calculations |
| 387 | if ($this->option_extra_info) { |
| 388 | $this->ChannelsBitratePlaytimeCalculations(); |
| 389 | $this->CalculateCompressionRatioVideo(); |
| 390 | $this->CalculateCompressionRatioAudio(); |
| 391 | $this->CalculateReplayGain(); |
| 392 | $this->ProcessAudioStreams(); |
| 393 | } |
| 394 | |
| 395 | // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags |
| 396 | if ($this->option_md5_data) { |
| 397 | // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too |
| 398 | if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { |
| 399 | $this->getHashdata('md5'); |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags |
| 404 | if ($this->option_sha1_data) { |
| 405 | $this->getHashdata('sha1'); |
| 406 | } |
| 407 | |
| 408 | // remove undesired keys |
| 409 | $this->CleanUp(); |
| 410 | |
| 411 | // restore magic_quotes_runtime setting |
| 412 | set_magic_quotes_runtime($old_magic_quotes_runtime); |
| 413 | |
| 414 | // return info array |
| 415 | return $this->info; |
| 416 | } |
| 417 | |
| 418 | |
| 419 | // private: error handling |
| 420 | function error($message) { |
| 421 | |
| 422 | $this->CleanUp(); |
| 423 | |
| 424 | $this->info['error'][] = $message; |
| 425 | return $this->info; |
| 426 | } |
| 427 | |
| 428 | |
| 429 | // private: warning handling |
| 430 | function warning($message) { |
| 431 | $this->info['warning'][] = $message; |
| 432 | return true; |
| 433 | } |
| 434 | |
| 435 | |
| 436 | // private: CleanUp |
| 437 | function CleanUp() { |
| 438 | |
| 439 | // remove possible empty keys |
| 440 | $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate'); |
| 441 | foreach ($AVpossibleEmptyKeys as $dummy => $key) { |
| 442 | if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) { |
| 443 | unset($this->info['audio'][$key]); |
| 444 | } |
| 445 | if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) { |
| 446 | unset($this->info['video'][$key]); |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | // remove empty root keys |
| 451 | if (!empty($this->info)) { |
| 452 | foreach ($this->info as $key => $value) { |
| 453 | if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) { |
| 454 | unset($this->info[$key]); |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | // remove meaningless entries from unknown-format files |
| 460 | if (empty($this->info['fileformat'])) { |
| 461 | if (isset($this->info['avdataoffset'])) { |
| 462 | unset($this->info['avdataoffset']); |
| 463 | } |
| 464 | if (isset($this->info['avdataend'])) { |
| 465 | unset($this->info['avdataend']); |
| 466 | } |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | |
| 471 | // return array containing information about all supported formats |
| 472 | function GetFileFormatArray() { |
| 473 | static $format_info = array(); |
| 474 | if (empty($format_info)) { |
| 475 | $format_info = array( |
| 476 | |
| 477 | // Audio formats |
| 478 | |
| 479 | // AC-3 - audio - Dolby AC-3 / Dolby Digital |
| 480 | 'ac3' => array( |
| 481 | 'pattern' => '^\x0B\x77', |
| 482 | 'group' => 'audio', |
| 483 | 'module' => 'ac3', |
| 484 | 'mime_type' => 'audio/ac3', |
| 485 | ), |
| 486 | |
| 487 | // AAC - audio - Advanced Audio Coding (AAC) - ADIF format |
| 488 | 'adif' => array( |
| 489 | 'pattern' => '^ADIF', |
| 490 | 'group' => 'audio', |
| 491 | 'module' => 'aac', |
| 492 | 'option' => 'adif', |
| 493 | 'mime_type' => 'application/octet-stream', |
| 494 | 'fail_ape' => 'WARNING', |
| 495 | ), |
| 496 | |
| 497 | |
| 498 | // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) |
| 499 | 'adts' => array( |
| 500 | 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', |
| 501 | 'group' => 'audio', |
| 502 | 'module' => 'aac', |
| 503 | 'option' => 'adts', |
| 504 | 'mime_type' => 'application/octet-stream', |
| 505 | 'fail_ape' => 'WARNING', |
| 506 | ), |
| 507 | |
| 508 | |
| 509 | // AU - audio - NeXT/Sun AUdio (AU) |
| 510 | 'au' => array( |
| 511 | 'pattern' => '^\.snd', |
| 512 | 'group' => 'audio', |
| 513 | 'module' => 'au', |
| 514 | 'mime_type' => 'audio/basic', |
| 515 | ), |
| 516 | |
| 517 | // AVR - audio - Audio Visual Research |
| 518 | 'avr' => array( |
| 519 | 'pattern' => '^2BIT', |
| 520 | 'group' => 'audio', |
| 521 | 'module' => 'avr', |
| 522 | 'mime_type' => 'application/octet-stream', |
| 523 | ), |
| 524 | |
| 525 | // BONK - audio - Bonk v0.9+ |
| 526 | 'bonk' => array( |
| 527 | 'pattern' => '^\x00(BONK|INFO|META| ID3)', |
| 528 | 'group' => 'audio', |
| 529 | 'module' => 'bonk', |
| 530 | 'mime_type' => 'audio/xmms-bonk', |
| 531 | ), |
| 532 | |
| 533 | // DSS - audio - Digital Speech Standard |
| 534 | 'dss' => array( |
| 535 | 'pattern' => '^[\x02]dss', |
| 536 | 'group' => 'audio', |
| 537 | 'module' => 'dss', |
| 538 | 'mime_type' => 'application/octet-stream', |
| 539 | ), |
| 540 | |
| 541 | // DTS - audio - Dolby Theatre System |
602 | | // FLAC - audio - Free Lossless Audio Codec |
603 | | 'flac' => array ( |
604 | | 'pattern' => '^fLaC', |
605 | | 'group' => 'audio', |
606 | | 'module' => 'xiph', |
607 | | 'mime_type' => 'audio/x-flac', |
608 | | ), |
609 | | |
610 | | // LA - audio - Lossless Audio (LA) |
611 | | 'la' => array ( |
612 | | 'pattern' => '^LA0[2-4]', |
613 | | 'group' => 'audio', |
614 | | 'module' => 'la', |
615 | | 'mime_type' => 'application/octet-stream', |
616 | | ), |
617 | | |
618 | | // LPAC - audio - Lossless Predictive Audio Compression (LPAC) |
619 | | 'lpac' => array ( |
620 | | 'pattern' => '^LPAC', |
621 | | 'group' => 'audio', |
622 | | 'module' => 'lpac', |
623 | | 'mime_type' => 'application/octet-stream', |
624 | | ), |
625 | | |
626 | | // MIDI - audio - MIDI (Musical Instrument Digital Interface) |
627 | | 'midi' => array ( |
628 | | 'pattern' => '^MThd', |
629 | | 'group' => 'audio', |
630 | | 'module' => 'midi', |
631 | | 'mime_type' => 'audio/midi', |
632 | | ), |
633 | | |
634 | | // MAC - audio - Monkey's Audio Compressor |
635 | | 'mac' => array ( |
636 | | 'pattern' => '^MAC ', |
637 | | 'group' => 'audio', |
638 | | 'module' => 'monkey', |
639 | | 'mime_type' => 'application/octet-stream', |
640 | | ), |
641 | | |
642 | | // MOD - audio - MODule (assorted sub-formats) |
643 | | 'mod' => array ( |
644 | | 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)', |
645 | | 'mime_type' => 'audio/mod', |
646 | | ), |
647 | | |
648 | | // MOD - audio - MODule (Impulse Tracker) |
649 | | 'it' => array ( |
650 | | 'pattern' => '^IMPM', |
651 | | 'mime_type' => 'audio/it', |
652 | | ), |
653 | | |
654 | | // MOD - audio - MODule (eXtended Module, various sub-formats) |
655 | | 'xm' => array ( |
656 | | 'pattern' => '^Extended Module', |
657 | | 'mime_type' => 'audio/xm', |
658 | | ), |
659 | | |
660 | | // MOD - audio - MODule (ScreamTracker) |
661 | | 's3m' => array ( |
662 | | 'pattern' => '^.{44}SCRM', |
663 | | 'mime_type' => 'audio/s3m', |
664 | | ), |
665 | | |
666 | | // MPC - audio - Musepack / MPEGplus SV7+ |
667 | | 'mpc' => array ( |
668 | | 'pattern' => '^(MP\+)', |
669 | | 'group' => 'audio', |
670 | | 'module' => 'mpc', |
671 | | 'mime_type' => 'audio/x-musepack', |
672 | | ), |
673 | | |
674 | | // MPC - audio - Musepack / MPEGplus SV4-6 |
675 | | 'mpc_old' => array ( |
676 | | 'pattern' => '^([\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])', |
677 | | 'group' => 'audio', |
678 | | 'module' => 'mpc_old', |
679 | | 'mime_type' => 'application/octet-stream', |
680 | | ), |
681 | | |
682 | | |
683 | | // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) |
684 | | 'mp3' => array ( |
685 | | 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', |
686 | | 'group' => 'audio', |
687 | | 'module' => 'mp3', |
688 | | 'mime_type' => 'audio/mpeg', |
689 | | ), |
690 | | |
691 | | // OFR - audio - OptimFROG |
692 | | 'ofr' => array ( |
693 | | 'pattern' => '^(\*RIFF|OFR)', |
694 | | 'group' => 'audio', |
695 | | 'module' => 'optimfrog', |
696 | | 'mime_type' => 'application/octet-stream', |
697 | | ), |
698 | | |
699 | | // RKAU - audio - RKive AUdio compressor |
700 | | 'rkau' => array ( |
701 | | 'pattern' => '^RKA', |
702 | | 'group' => 'audio', |
703 | | 'module' => 'rkau', |
704 | | 'mime_type' => 'application/octet-stream', |
705 | | ), |
706 | | |
707 | | // SHN - audio - Shorten |
708 | | 'shn' => array ( |
709 | | 'pattern' => '^ajkg', |
710 | | 'group' => 'audio', |
711 | | 'module' => 'shorten', |
712 | | 'mime_type' => 'audio/xmms-shn', |
713 | | 'fail_id3' => 'ERROR', |
714 | | 'fail_ape' => 'ERROR', |
715 | | ), |
716 | | |
717 | | // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) |
718 | | 'tta' => array ( |
719 | | 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' |
720 | | 'group' => 'audio', |
721 | | 'module' => 'tta', |
722 | | 'mime_type' => 'application/octet-stream', |
723 | | ), |
724 | | |
725 | | // VOC - audio - Creative Voice (VOC) |
726 | | 'voc' => array ( |
727 | | 'pattern' => '^Creative Voice File', |
728 | | 'group' => 'audio', |
729 | | 'module' => 'voc', |
730 | | 'mime_type' => 'audio/voc', |
731 | | ), |
732 | | |
733 | | // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) |
734 | | 'vqf' => array ( |
735 | | 'pattern' => '^TWIN', |
736 | | 'group' => 'audio', |
737 | | 'module' => 'vqf', |
738 | | 'mime_type' => 'application/octet-stream', |
739 | | ), |
740 | | |
741 | | // WV - audio - WavPack (v4.0+) |
742 | | 'vw' => array( |
743 | | 'pattern' => '^wvpk', |
744 | | 'group' => 'audio', |
745 | | 'module' => 'wavpack', |
746 | | 'mime_type' => 'application/octet-stream', |
747 | | ), |
748 | | |
749 | | |
750 | | // Audio-Video formats |
751 | | |
752 | | // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio |
753 | | 'asf' => array ( |
754 | | 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', |
755 | | 'group' => 'audio-video', |
756 | | 'module' => 'asf', |
757 | | 'mime_type' => 'video/x-ms-asf', |
758 | | ), |
759 | | |
760 | | // BINK - audio/video - Bink / Smacker |
761 | | 'bink' => array( |
762 | | 'pattern' => '^(BIK|SMK)', |
763 | | 'mime_type' => 'application/octet-stream', |
764 | | ), |
765 | | |
766 | | // FLV - audio/video - FLash Video |
767 | | 'flv' => array( |
768 | | 'pattern' => '^FLV\x01', |
769 | | 'group' => 'audio-video', |
770 | | 'module' => 'flv', |
771 | | 'mime_type' => 'video/x-flv', |
772 | | ), |
773 | | |
774 | | // MKAV - audio/video - Mastroka |
775 | | 'matroska' => array ( |
776 | | 'pattern' => '^\x1A\x45\xDF\xA3', |
777 | | 'mime_type' => 'application/octet-stream', |
778 | | ), |
779 | | |
780 | | // MPEG - audio/video - MPEG (Moving Pictures Experts Group) |
781 | | 'mpeg' => array ( |
782 | | 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', |
783 | | 'group' => 'audio-video', |
784 | | 'module' => 'mpeg', |
785 | | 'mime_type' => 'video/mpeg', |
786 | | ), |
787 | | |
788 | | // NSV - audio/video - Nullsoft Streaming Video (NSV) |
789 | | 'nsv' => array ( |
790 | | 'pattern' => '^NSV[sf]', |
791 | | 'group' => 'audio-video', |
792 | | 'module' => 'nsv', |
793 | | 'mime_type' => 'application/octet-stream', |
794 | | ), |
795 | | |
796 | | // Ogg - audio/video - Ogg (Ogg Vorbis, OggFLAC, Speex, Ogg Theora(*), Ogg Tarkin(*)) |
797 | | 'ogg' => array ( |
798 | | 'pattern' => '^OggS', |
799 | | 'group' => 'audio', |
800 | | 'module' => 'xiph', |
801 | | 'mime_type' => 'application/ogg', |
802 | | 'fail_id3' => 'WARNING', |
803 | | 'fail_ape' => 'WARNING', |
804 | | ), |
805 | | |
806 | | // QT - audio/video - Quicktime |
807 | | 'quicktime' => array ( |
808 | | 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', |
809 | | 'group' => 'audio-video', |
810 | | 'module' => 'quicktime', |
811 | | 'mime_type' => 'video/quicktime', |
812 | | ), |
813 | | |
814 | | // 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) |
815 | | 'riff' => array ( |
816 | | 'pattern' => '^(RIFF|SDSS|FORM)', |
817 | | 'group' => 'audio-video', |
818 | | 'module' => 'riff', |
819 | | 'mime_type' => 'audio/x-wave', |
820 | | 'fail_ape' => 'WARNING', |
821 | | ), |
822 | | |
823 | | // Real - audio/video - RealAudio, RealVideo |
824 | | 'real' => array ( |
825 | | 'pattern' => '^(\.RMF|.ra)', |
826 | | 'group' => 'audio-video', |
827 | | 'module' => 'real', |
828 | | 'mime_type' => 'audio/x-realaudio', |
829 | | ), |
830 | | |
831 | | // SWF - audio/video - ShockWave Flash |
832 | | 'swf' => array ( |
833 | | 'pattern' => '^(F|C)WS', |
834 | | 'group' => 'audio-video', |
835 | | 'module' => 'swf', |
836 | | 'mime_type' => 'application/x-shockwave-flash', |
837 | | ), |
838 | | |
839 | | |
840 | | // Still-Image formats |
841 | | |
842 | | // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) |
843 | | 'bmp' => array ( |
844 | | 'pattern' => '^BM', |
845 | | 'group' => 'graphic', |
846 | | 'module' => 'bmp', |
847 | | 'mime_type' => 'image/bmp', |
848 | | 'fail_id3' => 'ERROR', |
849 | | 'fail_ape' => 'ERROR', |
850 | | ), |
851 | | |
852 | | // GIF - still image - Graphics Interchange Format |
853 | | 'gif' => array ( |
854 | | 'pattern' => '^GIF', |
855 | | 'group' => 'graphic', |
856 | | 'module' => 'gif', |
857 | | 'mime_type' => 'image/gif', |
858 | | 'fail_id3' => 'ERROR', |
859 | | 'fail_ape' => 'ERROR', |
860 | | ), |
861 | | |
862 | | // JPEG - still image - Joint Photographic Experts Group (JPEG) |
863 | | 'jpeg' => array ( |
864 | | 'pattern' => '^\xFF\xD8\xFF', |
865 | | 'group' => 'graphic', |
866 | | 'module' => 'jpeg', |
867 | | 'mime_type' => 'image/jpeg', |
868 | | 'fail_id3' => 'ERROR', |
869 | | 'fail_ape' => 'ERROR', |
870 | | ), |
871 | | |
872 | | // PCD - still image - Kodak Photo CD |
873 | | 'pcd' => array ( |
874 | | 'pattern' => '^.{2048}PCD_IPI\x00', |
875 | | 'group' => 'graphic', |
876 | | 'module' => 'pcd', |
877 | | 'mime_type' => 'image/x-photo-cd', |
878 | | 'fail_id3' => 'ERROR', |
879 | | 'fail_ape' => 'ERROR', |
880 | | ), |
881 | | |
882 | | |
883 | | // PNG - still image - Portable Network Graphics (PNG) |
884 | | 'png' => array ( |
885 | | 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', |
886 | | 'group' => 'graphic', |
887 | | 'module' => 'png', |
888 | | 'mime_type' => 'image/png', |
889 | | 'fail_id3' => 'ERROR', |
890 | | 'fail_ape' => 'ERROR', |
891 | | ), |
| 549 | // FLAC - audio - Free Lossless Audio Codec |
| 550 | 'flac' => array( |
| 551 | 'pattern' => '^fLaC', |
| 552 | 'group' => 'audio', |
| 553 | 'module' => 'flac', |
| 554 | 'mime_type' => 'audio/x-flac', |
| 555 | ), |
| 556 | |
| 557 | // LA - audio - Lossless Audio (LA) |
| 558 | 'la' => array( |
| 559 | 'pattern' => '^LA0[2-4]', |
| 560 | 'group' => 'audio', |
| 561 | 'module' => 'la', |
| 562 | 'mime_type' => 'application/octet-stream', |
| 563 | ), |
| 564 | |
| 565 | // LPAC - audio - Lossless Predictive Audio Compression (LPAC) |
| 566 | 'lpac' => array( |
| 567 | 'pattern' => '^LPAC', |
| 568 | 'group' => 'audio', |
| 569 | 'module' => 'lpac', |
| 570 | 'mime_type' => 'application/octet-stream', |
| 571 | ), |
| 572 | |
| 573 | // MIDI - audio - MIDI (Musical Instrument Digital Interface) |
| 574 | 'midi' => array( |
| 575 | 'pattern' => '^MThd', |
| 576 | 'group' => 'audio', |
| 577 | 'module' => 'midi', |
| 578 | 'mime_type' => 'audio/midi', |
| 579 | ), |
| 580 | |
| 581 | // MAC - audio - Monkey's Audio Compressor |
| 582 | 'mac' => array( |
| 583 | 'pattern' => '^MAC ', |
| 584 | 'group' => 'audio', |
| 585 | 'module' => 'monkey', |
| 586 | 'mime_type' => 'application/octet-stream', |
| 587 | ), |
| 588 | |
| 589 | // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available |
| 590 | // // MOD - audio - MODule (assorted sub-formats) |
| 591 | // 'mod' => array( |
| 592 | // 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', |
| 593 | // 'group' => 'audio', |
| 594 | // 'module' => 'mod', |
| 595 | // 'option' => 'mod', |
| 596 | // 'mime_type' => 'audio/mod', |
| 597 | // ), |
| 598 | |
| 599 | // MOD - audio - MODule (Impulse Tracker) |
| 600 | 'it' => array( |
| 601 | 'pattern' => '^IMPM', |
| 602 | 'group' => 'audio', |
| 603 | 'module' => 'mod', |
| 604 | 'option' => 'it', |
| 605 | 'mime_type' => 'audio/it', |
| 606 | ), |
| 607 | |
| 608 | // MOD - audio - MODule (eXtended Module, various sub-formats) |
| 609 | 'xm' => array( |
| 610 | 'pattern' => '^Extended Module', |
| 611 | 'group' => 'audio', |
| 612 | 'module' => 'mod', |
| 613 | 'option' => 'xm', |
| 614 | 'mime_type' => 'audio/xm', |
| 615 | ), |
| 616 | |
| 617 | // MOD - audio - MODule (ScreamTracker) |
| 618 | 's3m' => array( |
| 619 | 'pattern' => '^.{44}SCRM', |
| 620 | 'group' => 'audio', |
| 621 | 'module' => 'mod', |
| 622 | 'option' => 's3m', |
| 623 | 'mime_type' => 'audio/s3m', |
| 624 | ), |
| 625 | |
| 626 | // MPC - audio - Musepack / MPEGplus |
| 627 | 'mpc' => array( |
| 628 | 'pattern' => '^(MPCK|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])', |
| 629 | 'group' => 'audio', |
| 630 | 'module' => 'mpc', |
| 631 | 'mime_type' => 'audio/x-musepack', |
| 632 | ), |
| 633 | |
| 634 | // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) |
| 635 | 'mp3' => array( |
| 636 | 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', |
| 637 | 'group' => 'audio', |
| 638 | 'module' => 'mp3', |
| 639 | 'mime_type' => 'audio/mpeg', |
| 640 | ), |
| 641 | |
| 642 | // OFR - audio - OptimFROG |
| 643 | 'ofr' => array( |
| 644 | 'pattern' => '^(\*RIFF|OFR)', |
| 645 | 'group' => 'audio', |
| 646 | 'module' => 'optimfrog', |
| 647 | 'mime_type' => 'application/octet-stream', |
| 648 | ), |
| 649 | |
| 650 | // RKAU - audio - RKive AUdio compressor |
| 651 | 'rkau' => array( |
| 652 | 'pattern' => '^RKA', |
| 653 | 'group' => 'audio', |
| 654 | 'module' => 'rkau', |
| 655 | 'mime_type' => 'application/octet-stream', |
| 656 | ), |
| 657 | |
| 658 | // SHN - audio - Shorten |
| 659 | 'shn' => array( |
| 660 | 'pattern' => '^ajkg', |
| 661 | 'group' => 'audio', |
| 662 | 'module' => 'shorten', |
| 663 | 'mime_type' => 'audio/xmms-shn', |
| 664 | 'fail_id3' => 'ERROR', |
| 665 | 'fail_ape' => 'ERROR', |
| 666 | ), |
| 667 | |
| 668 | // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) |
| 669 | 'tta' => array( |
| 670 | 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' |
| 671 | 'group' => 'audio', |
| 672 | 'module' => 'tta', |
| 673 | 'mime_type' => 'application/octet-stream', |
| 674 | ), |
| 675 | |
| 676 | // VOC - audio - Creative Voice (VOC) |
| 677 | 'voc' => array( |
| 678 | 'pattern' => '^Creative Voice File', |
| 679 | 'group' => 'audio', |
| 680 | 'module' => 'voc', |
| 681 | 'mime_type' => 'audio/voc', |
| 682 | ), |
| 683 | |
| 684 | // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) |
| 685 | 'vqf' => array( |
| 686 | 'pattern' => '^TWIN', |
| 687 | 'group' => 'audio', |
| 688 | 'module' => 'vqf', |
| 689 | 'mime_type' => 'application/octet-stream', |
| 690 | ), |
| 691 | |
| 692 | // WV - audio - WavPack (v4.0+) |
| 693 | 'wv' => array( |
| 694 | 'pattern' => '^wvpk', |
| 695 | 'group' => 'audio', |
| 696 | 'module' => 'wavpack', |
| 697 | 'mime_type' => 'application/octet-stream', |
| 698 | ), |
| 699 | |
| 700 | |
| 701 | // Audio-Video formats |
| 702 | |
| 703 | // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio |
| 704 | 'asf' => array( |
| 705 | 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', |
| 706 | 'group' => 'audio-video', |
| 707 | 'module' => 'asf', |
| 708 | 'mime_type' => 'video/x-ms-asf', |
| 709 | 'iconv_req' => false, |
| 710 | ), |
| 711 | |
| 712 | // BINK - audio/video - Bink / Smacker |
| 713 | 'bink' => array( |
| 714 | 'pattern' => '^(BIK|SMK)', |
| 715 | 'group' => 'audio-video', |
| 716 | 'module' => 'bink', |
| 717 | 'mime_type' => 'application/octet-stream', |
| 718 | ), |
| 719 | |
| 720 | // FLV - audio/video - FLash Video |
| 721 | 'flv' => array( |
| 722 | 'pattern' => '^FLV\x01', |
| 723 | 'group' => 'audio-video', |
| 724 | 'module' => 'flv', |
| 725 | 'mime_type' => 'video/x-flv', |
| 726 | ), |
| 727 | |
| 728 | // MKAV - audio/video - Mastroka |
| 729 | 'matroska' => array( |
| 730 | 'pattern' => '^\x1A\x45\xDF\xA3', |
| 731 | 'group' => 'audio-video', |
| 732 | 'module' => 'matroska', |
| 733 | 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska |
| 734 | ), |
| 735 | |
| 736 | // MPEG - audio/video - MPEG (Moving Pictures Experts Group) |
| 737 | 'mpeg' => array( |
| 738 | 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', |
| 739 | 'group' => 'audio-video', |
| 740 | 'module' => 'mpeg', |
| 741 | 'mime_type' => 'video/mpeg', |
| 742 | ), |
| 743 | |
| 744 | // NSV - audio/video - Nullsoft Streaming Video (NSV) |
| 745 | 'nsv' => array( |
| 746 | 'pattern' => '^NSV[sf]', |
| 747 | 'group' => 'audio-video', |
| 748 | 'module' => 'nsv', |
| 749 | 'mime_type' => 'application/octet-stream', |
| 750 | ), |
| 751 | |
| 752 | // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*)) |
| 753 | 'ogg' => array( |
| 754 | 'pattern' => '^OggS', |
| 755 | 'group' => 'audio', |
| 756 | 'module' => 'ogg', |
| 757 | 'mime_type' => 'application/ogg', |
| 758 | 'fail_id3' => 'WARNING', |
| 759 | 'fail_ape' => 'WARNING', |
| 760 | ), |
| 761 | |
| 762 | // QT - audio/video - Quicktime |
| 763 | 'quicktime' => array( |
| 764 | 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', |
| 765 | 'group' => 'audio-video', |
| 766 | 'module' => 'quicktime', |
| 767 | 'mime_type' => 'video/quicktime', |
| 768 | ), |
| 769 | |
| 770 | // 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) |
| 771 | 'riff' => array( |
| 772 | 'pattern' => '^(RIFF|SDSS|FORM)', |
| 773 | 'group' => 'audio-video', |
| 774 | 'module' => 'riff', |
| 775 | 'mime_type' => 'audio/x-wave', |
| 776 | 'fail_ape' => 'WARNING', |
| 777 | ), |
| 778 | |
| 779 | // Real - audio/video - RealAudio, RealVideo |
| 780 | 'real' => array( |
| 781 | 'pattern' => '^(\\.RMF|\\.ra)', |
| 782 | 'group' => 'audio-video', |
| 783 | 'module' => 'real', |
| 784 | 'mime_type' => 'audio/x-realaudio', |
| 785 | ), |
| 786 | |
| 787 | // SWF - audio/video - ShockWave Flash |
| 788 | 'swf' => array( |
| 789 | 'pattern' => '^(F|C)WS', |
| 790 | 'group' => 'audio-video', |
| 791 | 'module' => 'swf', |
| 792 | 'mime_type' => 'application/x-shockwave-flash', |
| 793 | ), |
| 794 | |
| 795 | |
| 796 | // Still-Image formats |
| 797 | |
| 798 | // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) |
| 799 | 'bmp' => array( |
| 800 | 'pattern' => '^BM', |
| 801 | 'group' => 'graphic', |
| 802 | 'module' => 'bmp', |
| 803 | 'mime_type' => 'image/bmp', |
| 804 | 'fail_id3' => 'ERROR', |
| 805 | 'fail_ape' => 'ERROR', |
| 806 | ), |
| 807 | |
| 808 | // GIF - still image - Graphics Interchange Format |
| 809 | 'gif' => array( |
| 810 | 'pattern' => '^GIF', |
| 811 | 'group' => 'graphic', |
| 812 | 'module' => 'gif', |
| 813 | 'mime_type' => 'image/gif', |
| 814 | 'fail_id3' => 'ERROR', |
| 815 | 'fail_ape' => 'ERROR', |
| 816 | ), |
| 817 | |
| 818 | // JPEG - still image - Joint Photographic Experts Group (JPEG) |
| 819 | 'jpg' => array( |
| 820 | 'pattern' => '^\xFF\xD8\xFF', |
| 821 | 'group' => 'graphic', |
| 822 | 'module' => 'jpg', |
| 823 | 'mime_type' => 'image/jpeg', |
| 824 | 'fail_id3' => 'ERROR', |
| 825 | 'fail_ape' => 'ERROR', |
| 826 | ), |
| 827 | |
| 828 | // PCD - still image - Kodak Photo CD |
| 829 | 'pcd' => array( |
| 830 | 'pattern' => '^.{2048}PCD_IPI\x00', |
| 831 | 'group' => 'graphic', |
| 832 | 'module' => 'pcd', |
| 833 | 'mime_type' => 'image/x-photo-cd', |
| 834 | 'fail_id3' => 'ERROR', |
| 835 | 'fail_ape' => 'ERROR', |
| 836 | ), |
| 837 | |
| 838 | |
| 839 | // PNG - still image - Portable Network Graphics (PNG) |
| 840 | 'png' => array( |
| 841 | 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', |
| 842 | 'group' => 'graphic', |
| 843 | 'module' => 'png', |
| 844 | 'mime_type' => 'image/png', |
| 845 | 'fail_id3' => 'ERROR', |
| 846 | 'fail_ape' => 'ERROR', |
| 847 | ), |
986 | | 'mime_type' => 'application/octet-stream', |
987 | | 'fail_id3' => 'ERROR', |
988 | | 'fail_ape' => 'ERROR', |
989 | | ), |
990 | | |
991 | | |
992 | | // PDF - data - Portable Document Format |
993 | | 'pdf' => array( |
994 | | 'pattern' => '^\x25PDF', |
995 | | 'mime_type' => 'application/pdf', |
996 | | 'fail_id3' => 'ERROR', |
997 | | 'fail_ape' => 'ERROR', |
998 | | ), |
999 | | |
1000 | | // DOC - data - Microsoft Word |
1001 | | 'msoffice' => array( |
1002 | | 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document |
1003 | | 'mime_type' => 'application/octet-stream', |
1004 | | 'fail_id3' => 'ERROR', |
1005 | | 'fail_ape' => 'ERROR', |
1006 | | ), |
1007 | | ); |
1008 | | |
1009 | | return $format_info; |
1010 | | } |
1011 | | |
1012 | | |
1013 | | |
1014 | | // Recursive over array - converts array to $encoding charset from $this->encoding |
1015 | | function CharConvert(&$array, $encoding) { |
1016 | | |
1017 | | // Identical encoding - end here |
1018 | | if ($encoding == $this->encoding) { |
1019 | | return; |
1020 | | } |
1021 | | |
1022 | | // Loop thru array |
1023 | | foreach ($array as $key => $value) { |
1024 | | |
1025 | | // Go recursive |
1026 | | if (is_array($value)) { |
1027 | | $this->CharConvert($array[$key], $encoding); |
1028 | | } |
1029 | | |
1030 | | // Convert string |
1031 | | elseif (is_string($value)) { |
1032 | | $array[$key] = $this->iconv($encoding, $this->encoding, $value); |
1033 | | } |
1034 | | } |
1035 | | } |
1036 | | |
1037 | | |
1038 | | |
1039 | | // Convert and copy tags |
1040 | | protected function HandleAllTags() { |
1041 | | |
1042 | | // Key name => array (tag name, character encoding) |
1043 | | static $tags = array ( |
1044 | | 'asf' => array ('asf', 'UTF-16LE'), |
1045 | | 'midi' => array ('midi', 'ISO-8859-1'), |
1046 | | 'nsv' => array ('nsv', 'ISO-8859-1'), |
1047 | | 'ogg' => array ('vorbiscomment', 'UTF-8'), |
1048 | | 'png' => array ('png', 'UTF-8'), |
1049 | | 'tiff' => array ('tiff', 'ISO-8859-1'), |
1050 | | 'quicktime' => array ('quicktime', 'ISO-8859-1'), |
1051 | | 'real' => array ('real', 'ISO-8859-1'), |
1052 | | 'vqf' => array ('vqf', 'ISO-8859-1'), |
1053 | | 'zip' => array ('zip', 'ISO-8859-1'), |
1054 | | 'riff' => array ('riff', 'ISO-8859-1'), |
1055 | | 'lyrics3' => array ('lyrics3', 'ISO-8859-1'), |
1056 | | 'id3v1' => array ('id3v1', ''), // change below - cannot assign variable to static array |
1057 | | 'id3v2' => array ('id3v2', 'UTF-8'), // module converts all frames to UTF-8 |
1058 | | 'ape' => array ('ape', 'UTF-8') |
1059 | | ); |
1060 | | $tags['id3v1'][1] = $this->encoding_id3v1; |
1061 | | |
1062 | | // Loop thru tags array |
1063 | | foreach ($tags as $comment_name => $tag_name_encoding_array) { |
1064 | | list($tag_name, $encoding) = $tag_name_encoding_array; |
1065 | | |
1066 | | // Fill in default encoding type if not already present |
1067 | | @$this->info[$comment_name] and $this->info[$comment_name]['encoding'] = $encoding; |
1068 | | |
1069 | | // Copy comments if key name set |
1070 | | if (@$this->info[$comment_name]['comments']) { |
1071 | | |
1072 | | foreach ($this->info[$comment_name]['comments'] as $tag_key => $value_array) { |
1073 | | foreach ($value_array as $key => $value) { |
1074 | | if (strlen(trim($value)) > 0) { |
1075 | | $this->info['tags'][$tag_name][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! |
1076 | | } |
1077 | | } |
1078 | | |
1079 | | } |
1080 | | |
1081 | | if (!@$this->info['tags'][$tag_name]) { |
1082 | | // comments are set but contain nothing but empty strings, so skip |
1083 | | continue; |
1084 | | } |
1085 | | |
1086 | | $this->CharConvert($this->info['tags'][$tag_name], $encoding); |
1087 | | } |
1088 | | } |
1089 | | |
1090 | | |
1091 | | // Merge comments from ['tags'] into common ['comments'] |
1092 | | if (@$this->info['tags']) { |
1093 | | |
1094 | | foreach ($this->info['tags'] as $tag_type => $tag_array) { |
1095 | | |
1096 | | foreach ($tag_array as $tag_name => $tagdata) { |
1097 | | |
1098 | | foreach ($tagdata as $key => $value) { |
1099 | | |
1100 | | if (!empty($value)) { |
1101 | | |
1102 | | if (empty($this->info['comments'][$tag_name])) { |
1103 | | |
1104 | | // fall through and append value |
1105 | | } |
1106 | | elseif ($tag_type == 'id3v1') { |
1107 | | |
1108 | | $new_value_length = strlen(trim($value)); |
1109 | | foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { |
1110 | | $old_value_length = strlen(trim($existing_value)); |
1111 | | if (($new_value_length <= $old_value_length) && (substr($existing_value, 0, $new_value_length) == trim($value))) { |
1112 | | // new value is identical but shorter-than (or equal-length to) one already in comments - skip |
1113 | | break 2; |
1114 | | } |
1115 | | } |
1116 | | } |
1117 | | else { |
1118 | | |
1119 | | $new_value_length = strlen(trim($value)); |
1120 | | foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) { |
1121 | | $old_value_length = strlen(trim($existing_value)); |
1122 | | if (($new_value_length > $old_value_length) && (substr(trim($value), 0, strlen($existing_value)) == $existing_value)) { |
1123 | | $this->info['comments'][$tag_name][$existing_key] = trim($value); |
1124 | | break 2; |
1125 | | } |
1126 | | } |
1127 | | } |
1128 | | |
1129 | | if (empty($this->info['comments'][$tag_name]) || !in_array(trim($value), $this->info['comments'][$tag_name])) { |
1130 | | $this->info['comments'][$tag_name][] = trim($value); |
1131 | | } |
1132 | | } |
1133 | | } |
1134 | | } |
1135 | | } |
1136 | | } |
1137 | | |
1138 | | return true; |
1139 | | } |
| 941 | 'group' => 'misc', |
| 942 | 'module' => 'par2', |
| 943 | 'mime_type' => 'application/octet-stream', |
| 944 | 'fail_id3' => 'ERROR', |
| 945 | 'fail_ape' => 'ERROR', |
| 946 | ), |
| 947 | |
| 948 | // PDF - data - Portable Document Format |
| 949 | 'pdf' => array( |
| 950 | 'pattern' => '^\x25PDF', |
| 951 | 'group' => 'misc', |
| 952 | 'module' => 'pdf', |
| 953 | 'mime_type' => 'application/pdf', |
| 954 | 'fail_id3' => 'ERROR', |
| 955 | 'fail_ape' => 'ERROR', |
| 956 | ), |
| 957 | |
| 958 | // MSOFFICE - data - ZIP compressed data |
| 959 | 'msoffice' => array( |
| 960 | 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document |
| 961 | 'group' => 'misc', |
| 962 | 'module' => 'msoffice', |
| 963 | 'mime_type' => 'application/octet-stream', |
| 964 | 'fail_id3' => 'ERROR', |
| 965 | 'fail_ape' => 'ERROR', |
| 966 | ), |
| 967 | ); |
| 968 | } |
| 969 | |
| 970 | return $format_info; |
| 971 | } |
| 972 | |
| 973 | |
| 974 | |
| 975 | function GetFileFormat(&$filedata, $filename='') { |
| 976 | // this function will determine the format of a file based on usually |
| 977 | // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, |
| 978 | // and in the case of ISO CD image, 6 bytes offset 32kb from the start |
| 979 | // of the file). |
| 980 | |
| 981 | // Identify file format - loop through $format_info and detect with reg expr |
| 982 | foreach ($this->GetFileFormatArray() as $format_name => $info) { |
| 983 | // Using preg_match() instead of ereg() - much faster |
| 984 | // The /s switch on preg_match() forces preg_match() NOT to treat |
| 985 | // newline (0x0A) characters as special chars but do a binary match |
| 986 | if (preg_match('/'.$info['pattern'].'/s', $filedata)) { |
| 987 | $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; |
| 988 | return $info; |
| 989 | } |
| 990 | } |
| 991 | |
| 992 | |
| 993 | if (preg_match('/\.mp[123a]$/i', $filename)) { |
| 994 | // Too many mp3 encoders on the market put gabage in front of mpeg files |
| 995 | // use assume format on these if format detection failed |
| 996 | $GetFileFormatArray = $this->GetFileFormatArray(); |
| 997 | $info = $GetFileFormatArray['mp3']; |
| 998 | $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; |
| 999 | return $info; |
| 1000 | } |
| 1001 | |
| 1002 | return false; |
| 1003 | } |
| 1004 | |
| 1005 | |
| 1006 | // converts array to $encoding charset from $this->encoding |
| 1007 | function CharConvert(&$array, $encoding) { |
| 1008 | |
| 1009 | // identical encoding - end here |
| 1010 | if ($encoding == $this->encoding) { |
| 1011 | return; |
| 1012 | } |
| 1013 | |
| 1014 | // loop thru array |
| 1015 | foreach ($array as $key => $value) { |
| 1016 | |
| 1017 | // go recursive |
| 1018 | if (is_array($value)) { |
| 1019 | $this->CharConvert($array[$key], $encoding); |
| 1020 | } |
| 1021 | |
| 1022 | // convert string |
| 1023 | elseif (is_string($value)) { |
| 1024 | $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value)); |
| 1025 | } |
| 1026 | } |
| 1027 | } |
| 1028 | |
| 1029 | |
| 1030 | function HandleAllTags() { |
| 1031 | |
| 1032 | // key name => array (tag name, character encoding) |
| 1033 | static $tags; |
| 1034 | if (empty($tags)) { |
| 1035 | $tags = array( |
| 1036 | 'asf' => array('asf' , 'UTF-16LE'), |
| 1037 | 'midi' => array('midi' , 'ISO-8859-1'), |
| 1038 | 'nsv' => array('nsv' , 'ISO-8859-1'), |
| 1039 | 'ogg' => array('vorbiscomment' , 'UTF-8'), |
| 1040 | 'png' => array('png' , 'UTF-8'), |
| 1041 | 'tiff' => array('tiff' , 'ISO-8859-1'), |
| 1042 | 'quicktime' => array('quicktime' , 'ISO-8859-1'), |
| 1043 | 'real' => array('real' , 'ISO-8859-1'), |
| 1044 | 'vqf' => array('vqf' , 'ISO-8859-1'), |
| 1045 | 'zip' => array('zip' , 'ISO-8859-1'), |
| 1046 | 'riff' => array('riff' , 'ISO-8859-1'), |
| 1047 | 'lyrics3' => array('lyrics3' , 'ISO-8859-1'), |
| 1048 | 'id3v1' => array('id3v1' , $this->encoding_id3v1), |
| 1049 | '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 |
| 1050 | 'ape' => array('ape' , 'UTF-8') |
| 1051 | ); |
| 1052 | } |
| 1053 | |
| 1054 | // loop thru comments array |
| 1055 | foreach ($tags as $comment_name => $tagname_encoding_array) { |
| 1056 | list($tag_name, $encoding) = $tagname_encoding_array; |
| 1057 | |
| 1058 | // fill in default encoding type if not already present |
| 1059 | if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) { |
| 1060 | $this->info[$comment_name]['encoding'] = $encoding; |
| 1061 | } |
| 1062 | |
| 1063 | // copy comments if key name set |
| 1064 | if (!empty($this->info[$comment_name]['comments'])) { |
| 1065 | |
| 1066 | foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { |
| 1067 | foreach ($valuearray as $key => $value) { |
| 1068 | if (strlen(trim($value)) > 0) { |
| 1069 | $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! |
| 1070 | } |
| 1071 | } |
| 1072 | } |
| 1073 | |
| 1074 | if (!isset($this->info['tags'][$tag_name])) { |
| 1075 | // comments are set but contain nothing but empty strings, so skip |
| 1076 | continue; |
| 1077 | } |
| 1078 | |
| 1079 | if ($this->option_tags_html) { |
| 1080 | foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { |
| 1081 | foreach ($valuearray as $key => $value) { |
| 1082 | if (is_string($value)) { |
| 1083 | //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding); |
| 1084 | $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $encoding)); |
| 1085 | } else { |
| 1086 | $this->info['tags_html'][$tag_name][$tag_key][$key] = $value; |
| 1087 | } |
| 1088 | } |
| 1089 | } |
| 1090 | } |
| 1091 | |
| 1092 | $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! |
| 1093 | } |
| 1094 | |
| 1095 | } |
| 1096 | return true; |
| 1097 | } |
| 1098 | |
| 1099 | |
| 1100 | function getHashdata($algorithm) { |
| 1101 | switch ($algorithm) { |
| 1102 | case 'md5': |
| 1103 | case 'sha1': |
| 1104 | break; |
| 1105 | |
| 1106 | default: |
| 1107 | return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()'); |
| 1108 | break; |
| 1109 | } |
| 1110 | |
| 1111 | if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) { |
| 1112 | |
| 1113 | // We cannot get an identical md5_data value for Ogg files where the comments |
| 1114 | // span more than 1 Ogg page (compared to the same audio data with smaller |
| 1115 | // comments) using the normal getID3() method of MD5'ing the data between the |
| 1116 | // end of the comments and the end of the file (minus any trailing tags), |
| 1117 | // because the page sequence numbers of the pages that the audio data is on |
| 1118 | // do not match. Under normal circumstances, where comments are smaller than |
| 1119 | // the nominal 4-8kB page size, then this is not a problem, but if there are |
| 1120 | // very large comments, the only way around it is to strip off the comment |
| 1121 | // tags with vorbiscomment and MD5 that file. |
| 1122 | // This procedure must be applied to ALL Ogg files, not just the ones with |
| 1123 | // comments larger than 1 page, because the below method simply MD5's the |
| 1124 | // whole file with the comments stripped, not just the portion after the |
| 1125 | // comments block (which is the standard getID3() method. |
| 1126 | |
| 1127 | // The above-mentioned problem of comments spanning multiple pages and changing |
| 1128 | // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but |
| 1129 | // currently vorbiscomment only works on OggVorbis files. |
| 1130 | |
| 1131 | if ((bool) ini_get('safe_mode')) { |
| 1132 | |
| 1133 | $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)'; |
| 1134 | $this->info[$algorithm.'_data'] = false; |
| 1135 | |
| 1136 | } else { |
| 1137 | |
| 1138 | // Prevent user from aborting script |
| 1139 | $old_abort = ignore_user_abort(true); |
| 1140 | |
| 1141 | // Create empty file |
| 1142 | $empty = tempnam('*', 'getID3'); |
| 1143 | touch($empty); |
| 1144 | |
| 1145 | |
| 1146 | // Use vorbiscomment to make temp file without comments |
| 1147 | $temp = tempnam('*', 'getID3'); |
| 1148 | $file = $this->info['filenamepath']; |
| 1149 | |
| 1150 | if (GETID3_OS_ISWINDOWS) { |
| 1151 | |
| 1152 | if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { |
| 1153 | |
| 1154 | $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"'; |
| 1155 | $VorbisCommentError = `$commandline`; |
| 1156 | |
| 1157 | } else { |
| 1158 | |
| 1159 | $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; |
| 1160 | |
| 1161 | } |
| 1162 | |
| 1163 | } else { |
| 1164 | |
| 1165 | $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; |
| 1166 | $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; |
| 1167 | $VorbisCommentError = `$commandline`; |
| 1168 | |
| 1169 | } |
| 1170 | |
| 1171 | if (!empty($VorbisCommentError)) { |
| 1172 | |
| 1173 | $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; |
| 1174 | $this->info[$algorithm.'_data'] = false; |
| 1175 | |
| 1176 | } else { |
| 1177 | |
| 1178 | // Get hash of newly created file |
| 1179 | switch ($algorithm) { |
| 1180 | case 'md5': |
| 1181 | $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp); |
| 1182 | break; |
| 1183 | |
| 1184 | case 'sha1': |
| 1185 | $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp); |
| 1186 | break; |
| 1187 | } |
| 1188 | } |
| 1189 | |
| 1190 | // Clean up |
| 1191 | unlink($empty); |
| 1192 | unlink($temp); |
| 1193 | |
| 1194 | // Reset abort setting |
| 1195 | ignore_user_abort($old_abort); |
| 1196 | |
| 1197 | } |
| 1198 | |
| 1199 | } else { |
| 1200 | |
| 1201 | if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) { |
| 1202 | |
| 1203 | // get hash from part of file |
| 1204 | $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm); |
| 1205 | |
| 1206 | } else { |
| 1207 | |
| 1208 | // get hash from whole file |
| 1209 | switch ($algorithm) { |
| 1210 | case 'md5': |
| 1211 | $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']); |
| 1212 | break; |
| 1213 | |
| 1214 | case 'sha1': |
| 1215 | $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']); |
| 1216 | break; |
| 1217 | } |
| 1218 | } |
| 1219 | |
| 1220 | } |
| 1221 | return true; |
| 1222 | } |
| 1223 | |
| 1224 | |
| 1225 | function ChannelsBitratePlaytimeCalculations() { |
| 1226 | |
| 1227 | // set channelmode on audio |
| 1228 | if (@$this->info['audio']['channels'] == '1') { |
| 1229 | $this->info['audio']['channelmode'] = 'mono'; |
| 1230 | } elseif (@$this->info['audio']['channels'] == '2') { |
| 1231 | $this->info['audio']['channelmode'] = 'stereo'; |
| 1232 | } |
| 1233 | |
| 1234 | // Calculate combined bitrate - audio + video |
| 1235 | $CombinedBitrate = 0; |
| 1236 | $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); |
| 1237 | $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); |
| 1238 | if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { |
| 1239 | $this->info['bitrate'] = $CombinedBitrate; |
| 1240 | } |
| 1241 | //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) { |
| 1242 | // // for example, VBR MPEG video files cannot determine video bitrate: |
| 1243 | // // should not set overall bitrate and playtime from audio bitrate only |
| 1244 | // unset($this->info['bitrate']); |
| 1245 | //} |
| 1246 | |
| 1247 | // video bitrate undetermined, but calculable |
| 1248 | if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) { |
| 1249 | // if video bitrate not set |
| 1250 | if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) { |
| 1251 | // AND if audio bitrate is set to same as overall bitrate |
| 1252 | if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) { |
| 1253 | // AND if playtime is set |
| 1254 | if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) { |
| 1255 | // AND if AV data offset start/end is known |
| 1256 | // THEN we can calculate the video bitrate |
| 1257 | $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']); |
| 1258 | $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate']; |
| 1259 | } |
| 1260 | } |
| 1261 | } |
| 1262 | } |
| 1263 | |
| 1264 | if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) { |
| 1265 | $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; |
| 1266 | } |
| 1267 | |
| 1268 | if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) { |
| 1269 | $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']; |
| 1270 | } |
| 1271 | //echo '<pre>'; |
| 1272 | //var_dump($this->info['bitrate']); |
| 1273 | //var_dump($this->info['audio']['bitrate']); |
| 1274 | //var_dump($this->info['video']['bitrate']); |
| 1275 | //echo '</pre>'; |
| 1276 | if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) { |
| 1277 | if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) { |
| 1278 | // audio only |
| 1279 | $this->info['audio']['bitrate'] = $this->info['bitrate']; |
| 1280 | } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) { |
| 1281 | // video only |
| 1282 | $this->info['video']['bitrate'] = $this->info['bitrate']; |
| 1283 | } |
| 1284 | } |
| 1285 | |
| 1286 | // Set playtime string |
| 1287 | if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { |
| 1288 | $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); |
| 1289 | } |
| 1290 | } |
| 1291 | |
| 1292 | |
| 1293 | function CalculateCompressionRatioVideo() { |
| 1294 | if (empty($this->info['video'])) { |
| 1295 | return false; |
| 1296 | } |
| 1297 | if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { |
| 1298 | return false; |
| 1299 | } |
| 1300 | if (empty($this->info['video']['bits_per_sample'])) { |
| 1301 | return false; |
| 1302 | } |
| 1303 | |
| 1304 | switch ($this->info['video']['dataformat']) { |
| 1305 | case 'bmp': |
| 1306 | case 'gif': |
| 1307 | case 'jpeg': |
| 1308 | case 'jpg': |
| 1309 | case 'png': |
| 1310 | case 'tiff': |
| 1311 | $FrameRate = 1; |
| 1312 | $PlaytimeSeconds = 1; |
| 1313 | $BitrateCompressed = $this->info['filesize'] * 8; |
| 1314 | break; |
| 1315 | |
| 1316 | default: |
| 1317 | if (!empty($this->info['video']['frame_rate'])) { |
| 1318 | $FrameRate = $this->info['video']['frame_rate']; |
| 1319 | } else { |
| 1320 | return false; |
| 1321 | } |
| 1322 | if (!empty($this->info['playtime_seconds'])) { |
| 1323 | $PlaytimeSeconds = $this->info['playtime_seconds']; |
| 1324 | } else { |
| 1325 | return false; |
| 1326 | } |
| 1327 | if (!empty($this->info['video']['bitrate'])) { |
| 1328 | $BitrateCompressed = $this->info['video']['bitrate']; |
| 1329 | } else { |
| 1330 | return false; |
| 1331 | } |
| 1332 | break; |
| 1333 | } |
| 1334 | $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; |
| 1335 | |
| 1336 | $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; |
| 1337 | return true; |
| 1338 | } |
| 1339 | |
| 1340 | |
| 1341 | function CalculateCompressionRatioAudio() { |
| 1342 | if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { |
| 1343 | return false; |
| 1344 | } |
| 1345 | $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)); |
| 1346 | |
| 1347 | if (!empty($this->info['audio']['streams'])) { |
| 1348 | foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { |
| 1349 | if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { |
| 1350 | $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); |
| 1351 | } |
| 1352 | } |
| 1353 | } |
| 1354 | return true; |
| 1355 | } |
| 1356 | |
| 1357 | |
| 1358 | function CalculateReplayGain() { |
| 1359 | if (isset($this->info['replay_gain'])) { |
| 1360 | $this->info['replay_gain']['reference_volume'] = 89; |
| 1361 | if (isset($this->info['replay_gain']['track']['adjustment'])) { |
| 1362 | $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; |
| 1363 | } |
| 1364 | if (isset($this->info['replay_gain']['album']['adjustment'])) { |
| 1365 | $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; |
| 1366 | } |
| 1367 | |
| 1368 | if (isset($this->info['replay_gain']['track']['peak'])) { |
| 1369 | $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); |
| 1370 | } |
| 1371 | if (isset($this->info['replay_gain']['album']['peak'])) { |
| 1372 | $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); |
| 1373 | } |
| 1374 | } |
| 1375 | return true; |
| 1376 | } |
| 1377 | |
| 1378 | function ProcessAudioStreams() { |
| 1379 | if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { |
| 1380 | if (!isset($this->info['audio']['streams'])) { |
| 1381 | foreach ($this->info['audio'] as $key => $value) { |
| 1382 | if ($key != 'streams') { |
| 1383 | $this->info['audio']['streams'][0][$key] = $value; |
| 1384 | } |
| 1385 | } |
| 1386 | } |
| 1387 | } |
| 1388 | return true; |
| 1389 | } |
| 1390 | |
| 1391 | function getid3_tempnam() { |
| 1392 | return tempnam($this->tempdir, 'gI3'); |
| 1393 | } |
| 1394 | |
1142 | | |
1143 | | abstract class getid3_handler |
1144 | | { |
1145 | | |
1146 | | protected $getid3; // pointer |
1147 | | |
1148 | | protected $data_string_flag = false; // analyzing filepointer or string |
1149 | | protected $data_string; // string to analyze |
1150 | | protected $data_string_position = 0; // seek position in string |
1151 | | |
1152 | | |
1153 | | public function __construct(getID3 $getid3) { |
1154 | | |
1155 | | $this->getid3 = $getid3; |
1156 | | } |
1157 | | |
1158 | | |
1159 | | // Analyze from file pointer |
1160 | | abstract public function Analyze(); |
1161 | | |
1162 | | |
1163 | | |
1164 | | // Analyze from string instead |
1165 | | public function AnalyzeString(&$string) { |
1166 | | |
1167 | | // Enter string mode |
1168 | | $this->data_string_flag = true; |
1169 | | $this->data_string = $string; |
1170 | | |
1171 | | // Save info |
1172 | | $saved_avdataoffset = $this->getid3->info['avdataoffset']; |
1173 | | $saved_avdataend = $this->getid3->info['avdataend']; |
1174 | | $saved_filesize = $this->getid3->info['filesize']; |
1175 | | |
1176 | | // Reset some info |
1177 | | $this->getid3->info['avdataoffset'] = 0; |
1178 | | $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string); |
1179 | | |
1180 | | // Analyze |
1181 | | $this->Analyze(); |
1182 | | |
1183 | | // Restore some info |
1184 | | $this->getid3->info['avdataoffset'] = $saved_avdataoffset; |
1185 | | $this->getid3->info['avdataend'] = $saved_avdataend; |
1186 | | $this->getid3->info['filesize'] = $saved_filesize; |
1187 | | |
1188 | | // Exit string mode |
1189 | | $this->data_string_flag = false; |
1190 | | } |
1191 | | |
1192 | | |
1193 | | protected function ftell() { |
1194 | | |
1195 | | if ($this->data_string_flag) { |
1196 | | return $this->data_string_position; |
1197 | | } |
1198 | | return ftell($this->getid3->fp); |
1199 | | } |
1200 | | |
1201 | | |
1202 | | protected function fread($bytes) { |
1203 | | |
1204 | | if ($this->data_string_flag) { |
1205 | | $this->data_string_position += $bytes; |
1206 | | return substr($this->data_string, $this->data_string_position - $bytes, $bytes); |
1207 | | } |
1208 | | return fread($this->getid3->fp, $bytes); |
1209 | | } |
1210 | | |
1211 | | |
1212 | | protected function fseek($bytes, $whence = SEEK_SET) { |
1213 | | |
1214 | | if ($this->data_string_flag) { |
1215 | | switch ($whence) { |
1216 | | case SEEK_SET: |
1217 | | $this->data_string_position = $bytes; |
1218 | | return; |
1219 | | |
1220 | | case SEEK_CUR: |
1221 | | $this->data_string_position += $bytes; |
1222 | | return; |
1223 | | |
1224 | | case SEEK_END: |
1225 | | $this->data_string_position = strlen($this->data_string) + $bytes; |
1226 | | return; |
1227 | | } |
1228 | | } |
1229 | | return fseek($this->getid3->fp, $bytes, $whence); |
1230 | | } |
1231 | | |
1232 | | } |
1233 | | |
1234 | | |
1235 | | |
1236 | | |
1237 | | abstract class getid3_handler_write |
1238 | | { |
1239 | | protected $filename; |
1240 | | protected $user_abort; |
1241 | | |
1242 | | private $fp_lock; |
1243 | | private $owner; |
1244 | | private $group; |
1245 | | private $perms; |
1246 | | |
1247 | | |
1248 | | public function __construct($filename) { |
1249 | | |
1250 | | if (!file_exists($filename)) { |
1251 | | throw new getid3_exception('File does not exist: "' . $filename . '"'); |
1252 | | } |
1253 | | |
1254 | | if (!is_writeable($filename)) { |
1255 | | throw new getid3_exception('File is not writeable: "' . $filename . '"'); |
1256 | | } |
1257 | | |
1258 | | if (!is_writeable(dirname($filename))) { |
1259 | | throw new getid3_exception('Directory is not writeable: ' . dirname($filename) . ' (need to write lock file).'); |
1260 | | } |
1261 | | |
1262 | | $this->user_abort = ignore_user_abort(true); |
1263 | | |
1264 | | $this->fp_lock = fopen($filename . '.getid3.lock', 'w'); |
1265 | | flock($this->fp_lock, LOCK_EX); |
1266 | | |
1267 | | $this->filename = $filename; |
1268 | | } |
1269 | | |
1270 | | |
1271 | | public function __destruct() { |
1272 | | |
1273 | | flock($this->fp_lock, LOCK_UN); |
1274 | | fclose($this->fp_lock); |
1275 | | unlink($this->filename . '.getid3.lock'); |
1276 | | |
1277 | | ignore_user_abort($this->user_abort); |
1278 | | } |
1279 | | |
1280 | | |
1281 | | protected function save_permissions() { |
1282 | | |
1283 | | $this->owner = fileowner($this->filename); |
1284 | | $this->group = filegroup($this->filename); |
1285 | | $this->perms = fileperms($this->filename); |
1286 | | } |
1287 | | |
1288 | | |
1289 | | protected function restore_permissions() { |
1290 | | |
1291 | | @chown($this->filename, $this->owner); |
1292 | | @chgrp($this->filename, $this->group); |
1293 | | @chmod($this->filename, $this->perms); |
1294 | | } |
1295 | | |
1296 | | |
1297 | | abstract public function read(); |
1298 | | |
1299 | | abstract public function write(); |
1300 | | |
1301 | | abstract public function remove(); |
1302 | | |
1303 | | } |
1304 | | |
1305 | | |
1306 | | |
1307 | | |
1308 | | class getid3_exception extends Exception |
1309 | | { |
1310 | | public $message; |
1311 | | |
1312 | | } |
1313 | | |
1314 | | |
1315 | | |
1316 | | |
1317 | | class getid3_lib |
1318 | | { |
1319 | | |
1320 | | // Convert Little Endian byte string to int - max 32 bits |
1321 | | public static function LittleEndian2Int($byte_word, $signed = false) { |
1322 | | |
1323 | | return getid3_lib::BigEndian2Int(strrev($byte_word), $signed); |
1324 | | } |
1325 | | |
1326 | | |
1327 | | |
1328 | | // Convert number to Little Endian byte string |
1329 | | public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { |
1330 | | $intstring = ''; |
1331 | | while ($number > 0) { |
1332 | | if ($synchsafe) { |
1333 | | $intstring = $intstring.chr($number & 127); |
1334 | | $number >>= 7; |
1335 | | } else { |
1336 | | $intstring = $intstring.chr($number & 255); |
1337 | | $number >>= 8; |
1338 | | } |
1339 | | } |
1340 | | return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); |
1341 | | } |
1342 | | |
1343 | | |
1344 | | |
1345 | | // Convert Big Endian byte string to int - max 32 bits |
1346 | | public static function BigEndian2Int($byte_word, $signed = false) { |
1347 | | |
1348 | | $int_value = 0; |
1349 | | $byte_wordlen = strlen($byte_word); |
1350 | | |
1351 | | for ($i = 0; $i < $byte_wordlen; $i++) { |
1352 | | $int_value += ord($byte_word{$i}) * pow(256, ($byte_wordlen - 1 - $i)); |
1353 | | } |
1354 | | |
1355 | | if ($signed) { |
1356 | | $sign_mask_bit = 0x80 << (8 * ($byte_wordlen - 1)); |
1357 | | if ($int_value & $sign_mask_bit) { |
1358 | | $int_value = 0 - ($int_value & ($sign_mask_bit - 1)); |
1359 | | } |
1360 | | } |
1361 | | |
1362 | | return $int_value; |
1363 | | } |
1364 | | |
1365 | | |
1366 | | |
1367 | | // Convert Big Endian byte sybc safe string to int - max 32 bits |
1368 | | public static function BigEndianSyncSafe2Int($byte_word) { |
1369 | | |
1370 | | $int_value = 0; |
1371 | | $byte_wordlen = strlen($byte_word); |
1372 | | |
1373 | | // disregard MSB, effectively 7-bit bytes |
1374 | | for ($i = 0; $i < $byte_wordlen; $i++) { |
1375 | | $int_value = $int_value | (ord($byte_word{$i}) & 0x7F) << (($byte_wordlen - 1 - $i) * 7); |
1376 | | } |
1377 | | return $int_value; |
1378 | | } |
1379 | | |
1380 | | |
1381 | | |
1382 | | // Convert Big Endian byte string to bit string |
1383 | | public static function BigEndian2Bin($byte_word) { |
1384 | | |
1385 | | $bin_value = ''; |
1386 | | $byte_wordlen = strlen($byte_word); |
1387 | | for ($i = 0; $i < $byte_wordlen; $i++) { |
1388 | | $bin_value .= str_pad(decbin(ord($byte_word{$i})), 8, '0', STR_PAD_LEFT); |
1389 | | } |
1390 | | return $bin_value; |
1391 | | } |
1392 | | |
1393 | | |
1394 | | |
1395 | | public static function BigEndian2Float($byte_word) { |
1396 | | |
1397 | | // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic |
1398 | | // http://www.psc.edu/general/software/packages/ieee/ieee.html |
1399 | | // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html |
1400 | | |
1401 | | $bit_word = getid3_lib::BigEndian2Bin($byte_word); |
1402 | | if (!$bit_word) { |
1403 | | return 0; |
1404 | | } |
1405 | | $sign_bit = $bit_word{0}; |
1406 | | |
1407 | | switch (strlen($byte_word) * 8) { |
1408 | | case 32: |
1409 | | $exponent_bits = 8; |
1410 | | $fraction_bits = 23; |
1411 | | break; |
1412 | | |
1413 | | case 64: |
1414 | | $exponent_bits = 11; |
1415 | | $fraction_bits = 52; |
1416 | | break; |
1417 | | |
1418 | | case 80: |
1419 | | // 80-bit Apple SANE format |
1420 | | // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ |
1421 | | $exponent_string = substr($bit_word, 1, 15); |
1422 | | $is_normalized = intval($bit_word{16}); |
1423 | | $fraction_string = substr($bit_word, 17, 63); |
1424 | | $exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383); |
1425 | | $fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string); |
1426 | | $float_value = $exponent * $fraction; |
1427 | | if ($sign_bit == '1') { |
1428 | | $float_value *= -1; |
1429 | | } |
1430 | | return $float_value; |
1431 | | break; |
1432 | | |
1433 | | default: |
1434 | | return false; |
1435 | | break; |
1436 | | } |
1437 | | $exponent_string = substr($bit_word, 1, $exponent_bits); |
1438 | | $fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits); |
1439 | | $exponent = bindec($exponent_string); |
1440 | | $fraction = bindec($fraction_string); |
1441 | | |
1442 | | if (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction != 0)) { |
1443 | | // Not a Number |
1444 | | $float_value = false; |
1445 | | } elseif (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction == 0)) { |
1446 | | if ($sign_bit == '1') { |
1447 | | $float_value = '-infinity'; |
1448 | | } else { |
1449 | | $float_value = '+infinity'; |
1450 | | } |
1451 | | } elseif (($exponent == 0) && ($fraction == 0)) { |
1452 | | if ($sign_bit == '1') { |
1453 | | $float_value = -0; |
1454 | | } else { |
1455 | | $float_value = 0; |
1456 | | } |
1457 | | $float_value = ($sign_bit ? 0 : -0); |
1458 | | } elseif (($exponent == 0) && ($fraction != 0)) { |
1459 | | // These are 'unnormalized' values |
1460 | | $float_value = pow(2, (-1 * (pow(2, $exponent_bits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fraction_string); |
1461 | | if ($sign_bit == '1') { |
1462 | | $float_value *= -1; |
1463 | | } |
1464 | | } elseif ($exponent != 0) { |
1465 | | $float_value = pow(2, ($exponent - (pow(2, $exponent_bits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fraction_string)); |
1466 | | if ($sign_bit == '1') { |
1467 | | $float_value *= -1; |
1468 | | } |
1469 | | } |
1470 | | return (float) $float_value; |
1471 | | } |
1472 | | |
1473 | | |
1474 | | |
1475 | | public static function LittleEndian2Float($byte_word) { |
1476 | | |
1477 | | return getid3_lib::BigEndian2Float(strrev($byte_word)); |
1478 | | } |
1479 | | |
1480 | | |
1481 | | |
1482 | | public static function DecimalBinary2Float($binary_numerator) { |
1483 | | $numerator = bindec($binary_numerator); |
1484 | | $denominator = bindec('1'.str_repeat('0', strlen($binary_numerator))); |
1485 | | return ($numerator / $denominator); |
1486 | | } |
1487 | | |
1488 | | |
1489 | | public static function PrintHexBytes($string, $hex=true, $spaces=true, $html_safe=true) { |
1490 | | |
1491 | | $return_string = ''; |
1492 | | for ($i = 0; $i < strlen($string); $i++) { |
1493 | | if ($hex) { |
1494 | | $return_string .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); |
1495 | | } else { |
1496 | | $return_string .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '¤'); |
1497 | | } |
1498 | | if ($spaces) { |
1499 | | $return_string .= ' '; |
1500 | | } |
1501 | | } |
1502 | | if ($html_safe) { |
1503 | | $return_string = htmlentities($return_string); |
1504 | | } |
1505 | | return $return_string; |
1506 | | } |
1507 | | |
1508 | | |
1509 | | |
1510 | | // Process header data string - read several values with algorithm and add to target |
1511 | | // algorithm is one one the getid3_lib::Something2Something() function names |
1512 | | // parts_array is index => length - $target[index] = algorithm(substring(data)) |
1513 | | // - OR just substring(data) if length is negative! |
1514 | | // indexes == 'IGNORE**' are ignored |
1515 | | |
1516 | | public static function ReadSequence($algorithm, &$target, &$data, $offset, $parts_array) { |
1517 | | |
1518 | | // Loop thru $parts_array |
1519 | | foreach ($parts_array as $target_string => $length) { |
1520 | | |
1521 | | // Add to target |
1522 | | if (!strstr($target_string, 'IGNORE')) { |
1523 | | |
1524 | | // substr(....length) |
1525 | | if ($length < 0) { |
1526 | | $target[$target_string] = substr($data, $offset, -$length); |
1527 | | } |
1528 | | |
1529 | | // algorithm(substr(...length)) |
1530 | | else { |
1531 | | $target[$target_string] = getid3_lib::$algorithm(substr($data, $offset, $length)); |
1532 | | } |
1533 | | } |
1534 | | |
1535 | | // Move pointer |
1536 | | $offset += abs($length); |
1537 | | } |
1538 | | } |
1539 | | |
1540 | | } |
1541 | | |
1542 | | |
1543 | | |
1544 | | class getid3_lib_replaygain |
1545 | | { |
1546 | | |
1547 | | public static function NameLookup($name_code) { |
1548 | | |
1549 | | static $lookup = array ( |
1550 | | 0 => 'not set', |
1551 | | 1 => 'Track Gain Adjustment', |
1552 | | 2 => 'Album Gain Adjustment' |
1553 | | ); |
1554 | | |
1555 | | return @$lookup[$name_code]; |
1556 | | } |
1557 | | |
1558 | | |
1559 | | |
1560 | | public static function OriginatorLookup($originator_code) { |
1561 | | |
1562 | | static $lookup = array ( |
1563 | | 0 => 'unspecified', |
1564 | | 1 => 'pre-set by artist/producer/mastering engineer', |
1565 | | 2 => 'set by user', |
1566 | | 3 => 'determined automatically' |
1567 | | ); |
1568 | | |
1569 | | return @$lookup[$originator_code]; |
1570 | | } |
1571 | | |
1572 | | |
1573 | | |
1574 | | public static function AdjustmentLookup($raw_adjustment, $sign_bit) { |
1575 | | |
1576 | | return (float)$raw_adjustment / 10 * ($sign_bit == 1 ? -1 : 1); |
1577 | | } |
1578 | | |
1579 | | |
1580 | | |
1581 | | public static function GainString($name_code, $originator_code, $replaygain) { |
1582 | | |
1583 | | $sign_bit = $replaygain < 0 ? 1 : 0; |
1584 | | |
1585 | | $stored_replaygain = intval(round($replaygain * 10)); |
1586 | | $gain_string = str_pad(decbin($name_code), 3, '0', STR_PAD_LEFT); |
1587 | | $gain_string .= str_pad(decbin($originator_code), 3, '0', STR_PAD_LEFT); |
1588 | | $gain_string .= $sign_bit; |
1589 | | $gain_string .= str_pad(decbin($stored_replaygain), 9, '0', STR_PAD_LEFT); |
1590 | | |
1591 | | return $gain_string; |
1592 | | } |
1593 | | |
1594 | | } |
1595 | | |
1596 | | |
1597 | | |
1598 | | |