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