ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Riff.php
Go to the documentation of this file.
1<?php
2
4
11
14// available at http://getid3.sourceforge.net //
15// or http://www.getid3.org //
17// See readme.txt for more details //
19// //
20// module.audio-video.riff.php //
21// module for analyzing RIFF files //
22// multiple formats supported by this module: //
23// Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX //
24// dependencies: module.audio.mp3.php //
25// module.audio.ac3.php (optional) //
26// module.audio.dts.php (optional) //
27// ///
29
42class Riff extends BaseHandler
43{
44
49 public function analyze()
50 {
51 $info = &$this->getid3->info;
52
53 // initialize these values to an empty array, otherwise they default to NULL
54 // and you can't append array values to a NULL value
55 $info['riff'] = array('raw'=>array());
56
57 // Shortcuts
58 $thisfile_riff = &$info['riff'];
59 $thisfile_riff_raw = &$thisfile_riff['raw'];
60 $thisfile_audio = &$info['audio'];
61 $thisfile_video = &$info['video'];
62 $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
63 $thisfile_riff_audio = &$thisfile_riff['audio'];
64 $thisfile_riff_video = &$thisfile_riff['video'];
65
66
67 $Original['avdataoffset'] = $info['avdataoffset'];
68 $Original['avdataend'] = $info['avdataend'];
69
70 fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
71 $RIFFheader = fread($this->getid3->fp, 12);
72 $RIFFsubtype = substr($RIFFheader, 8, 4);
73 switch (substr($RIFFheader, 0, 4)) {
74 case 'FORM':
75 $info['fileformat'] = 'aiff';
76 $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4));
77 $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']);
78 break;
79
80 case 'RIFF': // AVI, WAV, etc
81 case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
82 case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
83 $info['fileformat'] = 'riff';
84 $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4));
85 if ($RIFFsubtype == 'RMP3') {
86 // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
87 $RIFFsubtype = 'WAVE';
88 }
89 $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']);
90 if (($info['avdataend'] - $info['filesize']) == 1) {
91 // LiteWave appears to incorrectly *not* pad actual output file
92 // to nearest WORD boundary so may appear to be short by one
93 // byte, in which case - skip warning
94 $info['avdataend'] = $info['filesize'];
95 }
96
97 $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
98 while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
99 if (!Helper::intValueSupported($nextRIFFoffset + 1024)) {
100 $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
101 $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
102 break;
103 } else {
104 fseek($this->getid3->fp, $nextRIFFoffset, SEEK_SET);
105 $nextRIFFheader = fread($this->getid3->fp, 12);
106 if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
107 if (substr($nextRIFFheader, 0, 1) == "\x00") {
108 // RIFF padded to WORD boundary, we're actually already at the end
109 break;
110 }
111 }
112 $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
113 $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
114 $nextRIFFtype = substr($nextRIFFheader, 8, 4);
115 $chunkdata = array();
116 $chunkdata['offset'] = $nextRIFFoffset + 8;
117 $chunkdata['size'] = $nextRIFFsize;
118 $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
119 switch ($nextRIFFheaderID) {
120 case 'RIFF':
121 $info['avdataend'] = $nextRIFFoffset;
122 if (!Helper::intValueSupported($info['avdataend'])) {
123 $info['error'][] = 'AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
124 $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
125 }
126 $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $chunkdata['offset'] + $chunkdata['size']);
127
128 if (!isset($thisfile_riff[$nextRIFFtype])) {
129 $thisfile_riff[$nextRIFFtype] = array();
130 }
131 $thisfile_riff[$nextRIFFtype][] = $chunkdata;
132 break;
133 case 'JUNK':
134 // ignore
135 $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
136 break;
137 default:
138 if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
139 $DIVXTAG = $nextRIFFheader.fread($this->getid3->fp, 128 - 12);
140 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
141 // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
142 $info['warning'][] = 'Found wrongly-structured DIVXTAG at offset '.(ftell($this->getid3->fp) - 128 + 12).', parsing anyway';
143 $thisfile_riff['DIVXTAG'] = $this->ParseDIVXTAG($DIVXTAG);
144 foreach ($thisfile_riff['DIVXTAG'] as $key => $value) {
145 if ($value && !preg_match('#_id$#', $key)) {
146 $thisfile_riff['comments'][$key][] = $value;
147 }
148 }
149 break 2;
150 }
151 }
152 $info['warning'][] = 'expecting "RIFF" or "JUNK" at '.$nextRIFFoffset.', found '.Helper::PrintHexBytes(substr($nextRIFFheader, 0, 4)).' - skipping rest of file';
153 break 2;
154 }
155 }
156 }
157 if ($RIFFsubtype == 'WAVE') {
158 $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
159 }
160 break;
161
162 default:
163 $info['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead';
164 unset($info['fileformat']);
165
166 return false;
167 break;
168 }
169
170 $streamindex = 0;
171 switch ($RIFFsubtype) {
172 case 'WAVE':
173 if (empty($thisfile_audio['bitrate_mode'])) {
174 $thisfile_audio['bitrate_mode'] = 'cbr';
175 }
176 if (empty($thisfile_audio_dataformat)) {
177 $thisfile_audio_dataformat = 'wav';
178 }
179
180 if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
181 $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
182 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
183 }
184 if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
185
186 $thisfile_riff_audio[$streamindex] = self::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
187 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
188 if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
189 $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
190
191 return false;
192 }
193 $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
194 unset($thisfile_riff_audio[$streamindex]['raw']);
195 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
196
197 $thisfile_audio = Helper::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
198 if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
199 $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
200 }
201 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
202
203 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
204
205 $thisfile_audio['lossless'] = false;
206 if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
207 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
208
209 case 0x0001: // PCM
210 $thisfile_audio['lossless'] = true;
211 break;
212
213 case 0x2000: // AC-3
214 $thisfile_audio_dataformat = 'ac3';
215 break;
216
217 default:
218 // do nothing
219 break;
220
221 }
222 }
223 $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
224 $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
225 $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
226 $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
227 }
228
229 if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
230
231 // shortcuts
232 $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
233 $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array());
234 $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad'];
235 $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
236 $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
237
238 $thisfile_riff_raw_rgad['fPeakAmplitude'] = Helper::LittleEndian2Float(substr($rgadData, 0, 4));
239 $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
240 $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
241
242 $nRadioRgAdjustBitstring = str_pad(Helper::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
243 $nAudiophileRgAdjustBitstring = str_pad(Helper::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
244 $thisfile_riff_raw_rgad_track['name'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
245 $thisfile_riff_raw_rgad_track['originator'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
246 $thisfile_riff_raw_rgad_track['signbit'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
247 $thisfile_riff_raw_rgad_track['adjustment'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
248 $thisfile_riff_raw_rgad_album['name'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
249 $thisfile_riff_raw_rgad_album['originator'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
250 $thisfile_riff_raw_rgad_album['signbit'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
251 $thisfile_riff_raw_rgad_album['adjustment'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
252
253 $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
254 if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
255 $thisfile_riff['rgad']['track']['name'] = Helper::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
256 $thisfile_riff['rgad']['track']['originator'] = Helper::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
257 $thisfile_riff['rgad']['track']['adjustment'] = Helper::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
258 }
259 if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
260 $thisfile_riff['rgad']['album']['name'] = Helper::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
261 $thisfile_riff['rgad']['album']['originator'] = Helper::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
262 $thisfile_riff['rgad']['album']['adjustment'] = Helper::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
263 }
264 }
265
266 if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
267 $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
268
269 // This should be a good way of calculating exact playtime,
270 // but some sample files have had incorrect number of samples,
271 // so cannot use this method
272
273 // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
274 // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
275 // }
276 }
277 if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
278 $thisfile_audio['bitrate'] = Helper::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
279 }
280
281 if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
282 // shortcut
283 $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
284
285 $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
286 $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
287 $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
288 $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
289 $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
290 $thisfile_riff_WAVE_bext_0['time_reference'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
291 $thisfile_riff_WAVE_bext_0['bwf_version'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
292 $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
293 $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
294 if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
295 if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
296 list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
297 list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
298 $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']);
299 } else {
300 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
301 }
302 } else {
303 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
304 }
305 $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
306 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
307 }
308
309 if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
310 // shortcut
311 $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
312
313 $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
314 $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
315 if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
316 $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
317 $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
318 $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
319
320 $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
321 }
322 $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
323 $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
324 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
325 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
326 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
327 }
328
329 if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
330 // shortcut
331 $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
332
333 $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
334 $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
335 $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
336 $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
337 $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
338 $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
339 $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
340 $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
341 $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
342 $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
343 $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
344 $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
345 $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
346 $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
347 $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
348 $thisfile_riff_WAVE_cart_0['zero_db_reference'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
349 for ($i = 0; $i < 8; $i++) {
350 $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
351 $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
352 }
353 $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
354 $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
355
356 $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
357 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
358 }
359
360 if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
361 // SoundMiner metadata
362
363 // shortcuts
364 $thisfile_riff_WAVE_SNDM_0 = &$thisfile_riff_WAVE['SNDM'][0];
365 $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
366 $SNDM_startoffset = 0;
367 $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
368
369 while ($SNDM_startoffset < $SNDM_endoffset) {
370 $SNDM_thisTagOffset = 0;
371 $SNDM_thisTagSize = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
372 $SNDM_thisTagOffset += 4;
373 $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
374 $SNDM_thisTagOffset += 4;
375 $SNDM_thisTagDataSize = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
376 $SNDM_thisTagOffset += 2;
377 $SNDM_thisTagDataFlags = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
378 $SNDM_thisTagOffset += 2;
379 $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
380 $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
381
382 if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
383 $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
384 break;
385 } elseif ($SNDM_thisTagSize <= 0) {
386 $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
387 break;
388 }
389 $SNDM_startoffset += $SNDM_thisTagSize;
390
391 $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
392 if ($parsedkey = $this->RIFFwaveSNDMtagLookup($SNDM_thisTagKey)) {
393 $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
394 } else {
395 $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
396 }
397 }
398
399 $tagmapping = array(
400 'tracktitle'=>'title',
401 'category' =>'genre',
402 'cdtitle' =>'album',
403 'tracktitle'=>'title',
404 );
405 foreach ($tagmapping as $fromkey => $tokey) {
406 if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
407 $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
408 }
409 }
410 }
411
412 if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
413 // requires functions simplexml_load_string and get_object_vars
414 if ($parsedXML = Helper::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
415 $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
416 if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
417 @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
418 $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
419 }
420 if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
421 @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
422 $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
423 }
424 if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
425 $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
426 $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
427 $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
428 $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
429 $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
430 $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
431 $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
432 $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
433 }
434 unset($parsedXML);
435 }
436 }
437
438 if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
439 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
440 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
441 }
442
443 if (!empty($info['wavpack'])) {
444 $thisfile_audio_dataformat = 'wavpack';
445 $thisfile_audio['bitrate_mode'] = 'vbr';
446 $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
447
448 // Reset to the way it was - RIFF parsing will have messed this up
449 $info['avdataend'] = $Original['avdataend'];
450 $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
451
452 fseek($this->getid3->fp, $info['avdataoffset'] - 44, SEEK_SET);
453 $RIFFdata = fread($this->getid3->fp, 44);
454 $OrignalRIFFheaderSize = Helper::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
455 $OrignalRIFFdataSize = Helper::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
456
457 if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
458 $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
459 fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
460 $RIFFdata .= fread($this->getid3->fp, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
461 }
462
463 // move the data chunk after all other chunks (if any)
464 // so that the RIFF parser doesn't see EOF when trying
465 // to skip over the data chunk
466 $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
467 $getid3_riff = new self($this->getid3);
468 $getid3_riff->ParseRIFFdata($RIFFdata);
469 unset($getid3_riff);
470 }
471
472 if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
473 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
474 case 0x0001: // PCM
475 if (!empty($info['ac3'])) {
476 // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
477 $thisfile_audio['wformattag'] = 0x2000;
478 $thisfile_audio['codec'] = $this->RIFFwFormatTagLookup($thisfile_audio['wformattag']);
479 $thisfile_audio['lossless'] = false;
480 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
481 $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
482 }
483 break;
484 case 0x08AE: // ClearJump LiteWave
485 $thisfile_audio['bitrate_mode'] = 'vbr';
486 $thisfile_audio_dataformat = 'litewave';
487
488 //typedef struct tagSLwFormat {
489 // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags
490 // DWORD m_dwScale; // scale factor for lossy compression
491 // DWORD m_dwBlockSize; // number of samples in encoded blocks
492 // WORD m_wQuality; // alias for the scale factor
493 // WORD m_wMarkDistance; // distance between marks in bytes
494 // WORD m_wReserved;
495 //
496 // //following paramters are ignored if CF_FILESRC is not set
497 // DWORD m_dwOrgSize; // original file size in bytes
498 // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file
499 // DWORD m_dwRiffChunkSize; // riff chunk size in the original file
500 //
501 // PCMWAVEFORMAT m_OrgWf; // original wave format
502 // }SLwFormat, *PSLwFormat;
503
504 // shortcut
505 $thisfile_riff['litewave']['raw'] = array();
506 $thisfile_riff_litewave = &$thisfile_riff['litewave'];
507 $thisfile_riff_litewave_raw = &$thisfile_riff_litewave['raw'];
508
509 $thisfile_riff_litewave_raw['compression_method'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1));
510 $thisfile_riff_litewave_raw['compression_flags'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1));
511 $thisfile_riff_litewave_raw['m_dwScale'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4));
512 $thisfile_riff_litewave_raw['m_dwBlockSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4));
513 $thisfile_riff_litewave_raw['m_wQuality'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2));
514 $thisfile_riff_litewave_raw['m_wMarkDistance'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2));
515 $thisfile_riff_litewave_raw['m_wReserved'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2));
516 $thisfile_riff_litewave_raw['m_dwOrgSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4));
517 $thisfile_riff_litewave_raw['m_bFactExists'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2));
518 $thisfile_riff_litewave_raw['m_dwRiffChunkSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 40, 4));
519
520 //$thisfile_riff_litewave['quality_factor'] = intval(round((2000 - $thisfile_riff_litewave_raw['m_dwScale']) / 20));
521 $thisfile_riff_litewave['quality_factor'] = $thisfile_riff_litewave_raw['m_wQuality'];
522
523 $thisfile_riff_litewave['flags']['raw_source'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
524 $thisfile_riff_litewave['flags']['vbr_blocksize'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
525 $thisfile_riff_litewave['flags']['seekpoints'] = (bool) ($thisfile_riff_litewave_raw['compression_flags'] & 0x04);
526
527 $thisfile_audio['lossless'] = (($thisfile_riff_litewave_raw['m_wQuality'] == 100) ? true : false);
528 $thisfile_audio['encoder_options'] = '-q'.$thisfile_riff_litewave['quality_factor'];
529 break;
530
531 default:
532 break;
533 }
534 }
535 if ($info['avdataend'] > $info['filesize']) {
536 switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
537 case 'wavpack': // WavPack
538 case 'lpac': // LPAC
539 case 'ofr': // OptimFROG
540 case 'ofs': // OptimFROG DualStream
541 // lossless compressed audio formats that keep original RIFF headers - skip warning
542 break;
543
544 case 'litewave':
545 if (($info['avdataend'] - $info['filesize']) == 1) {
546 // LiteWave appears to incorrectly *not* pad actual output file
547 // to nearest WORD boundary so may appear to be short by one
548 // byte, in which case - skip warning
549 } else {
550 // Short by more than one byte, throw warning
551 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
552 $info['avdataend'] = $info['filesize'];
553 }
554 break;
555
556 default:
557 if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
558 // output file appears to be incorrectly *not* padded to nearest WORD boundary
559 // Output less severe warning
560 $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
561 $info['avdataend'] = $info['filesize'];
562 } else {
563 // Short by more than one byte, throw warning
564 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
565 $info['avdataend'] = $info['filesize'];
566 }
567 break;
568 }
569 }
570 if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
571 if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
572 $info['avdataend']--;
573 $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
574 }
575 }
576 if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
577 unset($thisfile_audio['bits_per_sample']);
578 if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
579 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
580 }
581 }
582 break;
583
584 case 'AVI ':
585 $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
586 $thisfile_video['dataformat'] = 'avi';
587 $info['mime_type'] = 'video/avi';
588
589 if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
590 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
591 if (isset($thisfile_riff['AVIX'])) {
592 $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
593 } else {
594 $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
595 }
596 if ($info['avdataend'] > $info['filesize']) {
597 $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
598 $info['avdataend'] = $info['filesize'];
599 }
600 }
601
602 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
603 //$bIndexType = array(
604 // 0x00 => 'AVI_INDEX_OF_INDEXES',
605 // 0x01 => 'AVI_INDEX_OF_CHUNKS',
606 // 0x80 => 'AVI_INDEX_IS_DATA',
607 //);
608 //$bIndexSubtype = array(
609 // 0x01 => array(
610 // 0x01 => 'AVI_INDEX_2FIELD',
611 // ),
612 //);
613 foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
614 $thisfile_riff_avi_hdrl_strl_indx_stream_data = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
615
616 $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 0, 2));
617 $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 2, 1));
618 $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 3, 1));
619 $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 4, 4));
620 $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 8, 4);
621 $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 12, 4));
622
623 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
624 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
625
626 unset($thisfile_riff_avi_hdrl_strl_indx_stream_data);
627 }
628 }
629 if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
630 $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
631
632 // shortcut
633 $thisfile_riff_raw['avih'] = array();
634 $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
635
636 $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
637 if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
638 $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
639
640 return false;
641 }
642 $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = $this->EitherEndian2Int(substr($avihData, 4, 4)); // max. transfer rate
643 $thisfile_riff_raw_avih['dwPaddingGranularity'] = $this->EitherEndian2Int(substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K.
644 $thisfile_riff_raw_avih['dwFlags'] = $this->EitherEndian2Int(substr($avihData, 12, 4)); // the ever-present flags
645 $thisfile_riff_raw_avih['dwTotalFrames'] = $this->EitherEndian2Int(substr($avihData, 16, 4)); // # frames in file
646 $thisfile_riff_raw_avih['dwInitialFrames'] = $this->EitherEndian2Int(substr($avihData, 20, 4));
647 $thisfile_riff_raw_avih['dwStreams'] = $this->EitherEndian2Int(substr($avihData, 24, 4));
648 $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($avihData, 28, 4));
649 $thisfile_riff_raw_avih['dwWidth'] = $this->EitherEndian2Int(substr($avihData, 32, 4));
650 $thisfile_riff_raw_avih['dwHeight'] = $this->EitherEndian2Int(substr($avihData, 36, 4));
651 $thisfile_riff_raw_avih['dwScale'] = $this->EitherEndian2Int(substr($avihData, 40, 4));
652 $thisfile_riff_raw_avih['dwRate'] = $this->EitherEndian2Int(substr($avihData, 44, 4));
653 $thisfile_riff_raw_avih['dwStart'] = $this->EitherEndian2Int(substr($avihData, 48, 4));
654 $thisfile_riff_raw_avih['dwLength'] = $this->EitherEndian2Int(substr($avihData, 52, 4));
655
656 $thisfile_riff_raw_avih['flags']['hasindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000010);
657 $thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000020);
658 $thisfile_riff_raw_avih['flags']['interleaved'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000100);
659 $thisfile_riff_raw_avih['flags']['trustcktype'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000800);
660 $thisfile_riff_raw_avih['flags']['capturedfile'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00010000);
661 $thisfile_riff_raw_avih['flags']['copyrighted'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00020010);
662
663 // shortcut
664 $thisfile_riff_video[$streamindex] = array();
665 $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
666
667 if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
668 $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
669 $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
670 }
671 if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
672 $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
673 $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
674 }
675 if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
676 $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
677 $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
678 }
679
680 $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
681 $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
682 }
683 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
684 if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
685 for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
686 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
687 $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
688 $strhfccType = substr($strhData, 0, 4);
689
690 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
691 $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
692
693 // shortcut
694 $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
695
696 switch ($strhfccType) {
697 case 'auds':
698 $thisfile_audio['bitrate_mode'] = 'cbr';
699 $thisfile_audio_dataformat = 'wav';
700 if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
701 $streamindex = count($thisfile_riff_audio);
702 }
703
704 $thisfile_riff_audio[$streamindex] = self::RIFFparseWAVEFORMATex($strfData);
705 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
706
707 // shortcut
708 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
709 $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
710
711 if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
712 unset($thisfile_audio_streams_currentstream['bits_per_sample']);
713 }
714 $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
715 unset($thisfile_audio_streams_currentstream['raw']);
716
717 // shortcut
718 $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
719
720 unset($thisfile_riff_audio[$streamindex]['raw']);
721 $thisfile_audio = Helper::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
722
723 $thisfile_audio['lossless'] = false;
724 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
725 case 0x0001: // PCM
726 $thisfile_audio_dataformat = 'wav';
727 $thisfile_audio['lossless'] = true;
728 break;
729
730 case 0x0050: // MPEG Layer 2 or Layer 1
731 $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
732 break;
733
734 case 0x0055: // MPEG Layer 3
735 $thisfile_audio_dataformat = 'mp3';
736 break;
737
738 case 0x00FF: // AAC
739 $thisfile_audio_dataformat = 'aac';
740 break;
741
742 case 0x0161: // Windows Media v7 / v8 / v9
743 case 0x0162: // Windows Media Professional v9
744 case 0x0163: // Windows Media Lossess v9
745 $thisfile_audio_dataformat = 'wma';
746 break;
747
748 case 0x2000: // AC-3
749 $thisfile_audio_dataformat = 'ac3';
750 break;
751
752 case 0x2001: // DTS
753 $thisfile_audio_dataformat = 'dts';
754 break;
755
756 default:
757 $thisfile_audio_dataformat = 'wav';
758 break;
759 }
760 $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
761 $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
762 $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
763 break;
764
765 case 'iavs':
766 case 'vids':
767 // shortcut
768 $thisfile_riff_raw['strh'][$i] = array();
769 $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
770
771 $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
772 $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
773 $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
774 $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
775 $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
776 $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
777 $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
778 $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
779 $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
780 $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
781 $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
782 $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
783 $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
784 $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
785
786 $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
787 $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
788 if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
789 $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
790 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
791 }
792 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
793 $thisfile_video['pixel_aspect_ratio'] = (float) 1;
794 switch ($thisfile_riff_raw_strh_current['fccHandler']) {
795 case 'HFYU': // Huffman Lossless Codec
796 case 'IRAW': // Intel YUV Uncompressed
797 case 'YUY2': // Uncompressed YUV 4:2:2
798 $thisfile_video['lossless'] = true;
799 break;
800
801 default:
802 $thisfile_video['lossless'] = false;
803 break;
804 }
805
806 switch ($strhfccType) {
807 case 'vids':
808 $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($info['fileformat'] == 'riff'));
809//echo '<pre>'.print_r($thisfile_riff_raw_strf_strhfccType_streamindex, true).'</pre>';
810 $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
811
812 if ($thisfile_riff_video_current['codec'] == 'DV') {
813 $thisfile_riff_video_current['dv_type'] = 2;
814 }
815 break;
816
817 case 'iavs':
818 $thisfile_riff_video_current['dv_type'] = 1;
819 break;
820 }
821 break;
822
823 default:
824 $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
825 break;
826
827 }
828 }
829 }
830
831 if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
832
833 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
834 if (self::RIFFfourccLookup($thisfile_video['fourcc'])) {
835 $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_video['fourcc']);
836 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
837 }
838
839 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
840 case 'HFYU': // Huffman Lossless Codec
841 case 'IRAW': // Intel YUV Uncompressed
842 case 'YUY2': // Uncompressed YUV 4:2:2
843 $thisfile_video['lossless'] = true;
844 //$thisfile_video['bits_per_sample'] = 24;
845 break;
846
847 default:
848 $thisfile_video['lossless'] = false;
849 //$thisfile_video['bits_per_sample'] = 24;
850 break;
851 }
852
853 }
854 }
855 }
856 }
857 break;
858
859 case 'CDDA':
860 $thisfile_audio['bitrate_mode'] = 'cbr';
861 $thisfile_audio_dataformat = 'cda';
862 $thisfile_audio['lossless'] = true;
863 unset($info['mime_type']);
864
865 $info['avdataoffset'] = 44;
866
867 if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
868 // shortcut
869 $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
870
871 $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
872 $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
873 $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
874 $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
875 $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
876 $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
877 $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
878
879 $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
880 $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
881 $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
882 $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
883
884 // hardcoded data for CD-audio
885 $thisfile_audio['sample_rate'] = 44100;
886 $thisfile_audio['channels'] = 2;
887 $thisfile_audio['bits_per_sample'] = 16;
888 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
889 $thisfile_audio['bitrate_mode'] = 'cbr';
890 }
891 break;
892
893 case 'AIFF':
894 case 'AIFC':
895 $thisfile_audio['bitrate_mode'] = 'cbr';
896 $thisfile_audio_dataformat = 'aiff';
897 $thisfile_audio['lossless'] = true;
898 $info['mime_type'] = 'audio/x-aiff';
899
900 if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
901 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
902 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
903 if ($info['avdataend'] > $info['filesize']) {
904 if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
905 // structures rounded to 2-byte boundary, but dumb encoders
906 // forget to pad end of file to make this actually work
907 } else {
908 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
909 }
910 $info['avdataend'] = $info['filesize'];
911 }
912 }
913
914 if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
915
916 // shortcut
917 $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
918
919 $thisfile_riff_audio['channels'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
920 $thisfile_riff_audio['total_samples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
921 $thisfile_riff_audio['bits_per_sample'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
922 $thisfile_riff_audio['sample_rate'] = (int) Helper::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
923
924 if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
925 $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
926 $CodecNameSize = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
927 $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
928 switch ($thisfile_riff_audio['codec_name']) {
929 case 'NONE':
930 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
931 $thisfile_audio['lossless'] = true;
932 break;
933
934 case '':
935 switch ($thisfile_riff_audio['codec_fourcc']) {
936 // http://developer.apple.com/qa/snd/snd07.html
937 case 'sowt':
938 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
939 $thisfile_audio['lossless'] = true;
940 break;
941
942 case 'twos':
943 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
944 $thisfile_audio['lossless'] = true;
945 break;
946
947 default:
948 break;
949 }
950 break;
951
952 default:
953 $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
954 $thisfile_audio['lossless'] = false;
955 break;
956 }
957 }
958
959 $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
960 if ($thisfile_riff_audio['bits_per_sample'] > 0) {
961 $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
962 }
963 $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
964 if ($thisfile_audio['sample_rate'] == 0) {
965 $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
966
967 return false;
968 }
969 $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
970 }
971
972 if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
973 $offset = 0;
974 $CommentCount = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
975 $offset += 2;
976 for ($i = 0; $i < $CommentCount; $i++) {
977 $info['comments_raw'][$i]['timestamp'] = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
978 $offset += 4;
979 $info['comments_raw'][$i]['marker_id'] = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
980 $offset += 2;
981 $CommentLength = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
982 $offset += 2;
983 $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
984 $offset += $CommentLength;
985
986 $info['comments_raw'][$i]['timestamp_unix'] = Helper::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
987 $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
988 }
989 }
990
991 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
992 foreach ($CommentsChunkNames as $key => $value) {
993 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
994 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
995 }
996 }
997
998 break;
999
1000 case '8SVX':
1001 $thisfile_audio['bitrate_mode'] = 'cbr';
1002 $thisfile_audio_dataformat = '8svx';
1003 $thisfile_audio['bits_per_sample'] = 8;
1004 $thisfile_audio['channels'] = 1; // overridden below, if need be
1005 $info['mime_type'] = 'audio/x-aiff';
1006
1007 if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1008 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1009 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1010 if ($info['avdataend'] > $info['filesize']) {
1011 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1012 }
1013 }
1014
1015 if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1016 // shortcut
1017 $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1018
1019 $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1020 $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1021 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1022 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1023 $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1024 $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1025 $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = Helper::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1026
1027 $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1028
1029 switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1030 case 0:
1031 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1032 $thisfile_audio['lossless'] = true;
1033 $ActualBitsPerSample = 8;
1034 break;
1035
1036 case 1:
1037 $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1038 $thisfile_audio['lossless'] = false;
1039 $ActualBitsPerSample = 4;
1040 break;
1041
1042 default:
1043 $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1044 break;
1045 }
1046 }
1047
1048 if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1049 $ChannelsIndex = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1050 switch ($ChannelsIndex) {
1051 case 6: // Stereo
1052 $thisfile_audio['channels'] = 2;
1053 break;
1054
1055 case 2: // Left channel only
1056 case 4: // Right channel only
1057 $thisfile_audio['channels'] = 1;
1058 break;
1059
1060 default:
1061 $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1062 break;
1063 }
1064
1065 }
1066
1067 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1068 foreach ($CommentsChunkNames as $key => $value) {
1069 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1070 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1071 }
1072 }
1073
1074 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1075 if (!empty($thisfile_audio['bitrate'])) {
1076 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1077 }
1078 break;
1079
1080 case 'CDXA':
1081 $info['mime_type'] = 'video/mpeg';
1082 if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1083 if (class_exists('GetId3\Module\AudioVideo\Mpeg')) {
1084 $getid3_temp = new GetId3Core();
1085 $getid3_temp->openfile($this->getid3->filename);
1086 $getid3_mpeg = new Mpeg($getid3_temp);
1087 $getid3_mpeg->analyze();
1088 if (empty($getid3_temp->info['error'])) {
1089 $info['audio'] = $getid3_temp->info['audio'];
1090 $info['video'] = $getid3_temp->info['video'];
1091 $info['mpeg'] = $getid3_temp->info['mpeg'];
1092 $info['warning'] = $getid3_temp->info['warning'];
1093 }
1094 unset($getid3_temp, $getid3_mpeg);
1095 }
1096 }
1097 break;
1098
1099 default:
1100 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1101 unset($info['fileformat']);
1102 break;
1103 }
1104
1105 switch ($RIFFsubtype) {
1106 case 'WAVE':
1107 case 'AIFF':
1108 case 'AIFC':
1109 if (isset($thisfile_riff[$RIFFsubtype]['ID3 ']) && !array_key_exists('id3 ', $thisfile_riff[$RIFFsubtype])) {
1110 $info['warning'][] = 'mapping "ID3 " chunk to "id3 "';
1111 }
1112 if (isset($thisfile_riff[$RIFFsubtype]['tag ']) && !array_key_exists('id3 ', $thisfile_riff[$RIFFsubtype])) {
1113 $info['warning'][] = 'mapping "tag " chunk to "id3 "';
1114 }
1115 if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1116
1117 $getid3_temp = new GetId3Core();
1118 $getid3_temp->openfile($this->getid3->filename);
1119 $getid3_id3v2 = new Id3v2($getid3_temp);
1120 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1121 if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->analyze()) {
1122 $info['id3v2'] = $getid3_temp->info['id3v2'];
1123 }
1124 unset($getid3_temp, $getid3_id3v2);
1125 }
1126 break;
1127 }
1128
1129 if (isset($thisfile_riff_raw['fmt ']['wFormatTag']) && ($thisfile_riff_raw['fmt ']['wFormatTag'] == 1)) {
1130 // http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html
1131 fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
1132 $FirstFourBytes = fread($this->getid3->fp, 4);
1133 if (preg_match('/^\xFF\x1F\x00\xE8/s', $FirstFourBytes)) {
1134 // DTSWAV
1135 $thisfile_audio_dataformat = 'dts';
1136 } elseif (preg_match('/^\x7F\xFF\x80\x01/s', $FirstFourBytes)) {
1137 // DTS, but this probably shouldn't happen
1138 $thisfile_audio_dataformat = 'dts';
1139 }
1140 }
1141
1142
1143 if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1144 $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1145 }
1146 if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1147 $this->RIFFcommentsParse($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1148 }
1149 if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1150 $this->RIFFcommentsParse($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1151 }
1152
1153 if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1154 $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1155 }
1156
1157 if (!isset($info['playtime_seconds'])) {
1158 $info['playtime_seconds'] = 0;
1159 }
1160 if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1161 // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1162 $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1163 } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1164 $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1165 }
1166
1167 if ($info['playtime_seconds'] > 0) {
1168 if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1169
1170 if (!isset($info['bitrate'])) {
1171 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1172 }
1173
1174 } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1175
1176 if (!isset($thisfile_audio['bitrate'])) {
1177 $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1178 }
1179
1180 } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1181
1182 if (!isset($thisfile_video['bitrate'])) {
1183 $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1184 }
1185
1186 }
1187 }
1188
1189
1190 if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1191
1192 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1193 $thisfile_audio['bitrate'] = 0;
1194 $thisfile_video['bitrate'] = $info['bitrate'];
1195 foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1196 $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1197 $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1198 }
1199 if ($thisfile_video['bitrate'] <= 0) {
1200 unset($thisfile_video['bitrate']);
1201 }
1202 if ($thisfile_audio['bitrate'] <= 0) {
1203 unset($thisfile_audio['bitrate']);
1204 }
1205 }
1206
1207 if (isset($info['mpeg']['audio'])) {
1208 $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1209 $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1210 $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1211 $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1212 $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1213 if (!empty($info['mpeg']['audio']['codec'])) {
1214 $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1215 }
1216 if (!empty($thisfile_audio['streams'])) {
1217 foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1218 if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1219 $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1220 $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1221 $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1222 $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1223 $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1224 }
1225 }
1226 }
1227 $getid3_mp3 = new Mp3($this->getid3);
1228 $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1229 unset($getid3_mp3);
1230 }
1231
1232
1233 if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1234 switch ($thisfile_audio_dataformat) {
1235 case 'ac3':
1236 // ignore bits_per_sample
1237 break;
1238
1239 default:
1240 $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1241 break;
1242 }
1243 }
1244
1245
1246 if (empty($thisfile_riff_raw)) {
1247 unset($thisfile_riff['raw']);
1248 }
1249 if (empty($thisfile_riff_audio)) {
1250 unset($thisfile_riff['audio']);
1251 }
1252 if (empty($thisfile_riff_video)) {
1253 unset($thisfile_riff['video']);
1254 }
1255
1256 return true;
1257 }
1258
1265 public static function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray)
1266 {
1267 $RIFFinfoKeyLookup = array(
1268 'IARL'=>'archivallocation',
1269 'IART'=>'artist',
1270 'ICDS'=>'costumedesigner',
1271 'ICMS'=>'commissionedby',
1272 'ICMT'=>'comment',
1273 'ICNT'=>'country',
1274 'ICOP'=>'copyright',
1275 'ICRD'=>'creationdate',
1276 'IDIM'=>'dimensions',
1277 'IDIT'=>'digitizationdate',
1278 'IDPI'=>'resolution',
1279 'IDST'=>'distributor',
1280 'IEDT'=>'editor',
1281 'IENG'=>'engineers',
1282 'IFRM'=>'accountofparts',
1283 'IGNR'=>'genre',
1284 'IKEY'=>'keywords',
1285 'ILGT'=>'lightness',
1286 'ILNG'=>'language',
1287 'IMED'=>'orignalmedium',
1288 'IMUS'=>'composer',
1289 'INAM'=>'title',
1290 'IPDS'=>'productiondesigner',
1291 'IPLT'=>'palette',
1292 'IPRD'=>'product',
1293 'IPRO'=>'producer',
1294 'IPRT'=>'part',
1295 'IRTD'=>'rating',
1296 'ISBJ'=>'subject',
1297 'ISFT'=>'software',
1298 'ISGN'=>'secondarygenre',
1299 'ISHP'=>'sharpness',
1300 'ISRC'=>'sourcesupplier',
1301 'ISRF'=>'digitizationsource',
1302 'ISTD'=>'productionstudio',
1303 'ISTR'=>'starring',
1304 'ITCH'=>'encoded_by',
1305 'IWEB'=>'url',
1306 'IWRI'=>'writer'
1307 );
1308 foreach ($RIFFinfoKeyLookup as $key => $value) {
1309 if (isset($RIFFinfoArray[$key])) {
1310 foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1311 if (trim($commentdata['data']) != '') {
1312 if (isset($CommentsTargetArray[$value])) {
1313 $CommentsTargetArray[$value][] = trim($commentdata['data']);
1314 } else {
1315 $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1316 }
1317 }
1318 }
1319 }
1320 }
1321
1322 return true;
1323 }
1324
1331 public function ParseRIFF($startoffset, $maxoffset)
1332 {
1333 $info = &$this->getid3->info;
1334
1335 $maxoffset = min($maxoffset, $info['avdataend']);
1336
1337 $RIFFchunk = false;
1338 $FoundAllChunksWeNeed = false;
1339
1340 if (($startoffset < 0) || !Helper::intValueSupported($startoffset)) {
1341 $info['warning'][] = 'Unable to ParseRIFF() at '.$startoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1342
1343 return false;
1344 }
1345 $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset);
1346 if ($maxoffset > $max_usable_offset) {
1347 $info['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at '.$startoffset.' because beyond it extends to '.$maxoffset.', which is beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1348 }
1349 fseek($this->getid3->fp, $startoffset, SEEK_SET);
1350
1351 while (ftell($this->getid3->fp) < $max_usable_offset) {
1352 $chunknamesize = fread($this->getid3->fp, 8);
1353 $chunkname = substr($chunknamesize, 0, 4);
1354 $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1355 if (strlen($chunkname) < 4) {
1356 $info['error'][] = 'Expecting chunk name at offset '.(ftell($this->getid3->fp) - 4).' but found nothing. Aborting RIFF parsing.';
1357 break;
1358 }
1359 if ($chunksize == 0) {
1360 if ($chunkname == 'JUNK') {
1361 // we'll allow zero-size JUNK frames
1362 } else {
1363 $info['warning'][] = 'Chunk size at offset '.(ftell($this->getid3->fp) - 4).' is zero. Aborting RIFF parsing.';
1364 break;
1365 }
1366 }
1367 if (($chunksize % 2) != 0) {
1368 // all structures are packed on word boundaries
1369 $chunksize++;
1370 }
1371
1372 switch ($chunkname) {
1373 case 'LIST':
1374 $listname = fread($this->getid3->fp, 4);
1375 if (preg_match('#^(movi|rec )$#i', $listname)) {
1376 $RIFFchunk[$listname]['offset'] = ftell($this->getid3->fp) - 4;
1377 $RIFFchunk[$listname]['size'] = $chunksize;
1378
1379 if ($FoundAllChunksWeNeed) {
1380
1381 // skip over
1382
1383 } else {
1384
1385 $WhereWeWere = ftell($this->getid3->fp);
1386 $AudioChunkHeader = fread($this->getid3->fp, 12);
1387 $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1388 $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1389 $AudioChunkSize = Helper::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1390
1391 if ($AudioChunkStreamType == 'wb') {
1392 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1393 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1394 // MP3
1395 if (GGetId3\Module\Audio\Mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1396 $getid3_temp = new GetId3Core();
1397 $getid3_temp->openfile($this->getid3->filename);
1398 $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
1399 $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
1400 $getid3_mp3 = new Mp3($getid3_temp);
1401 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1402 if (isset($getid3_temp->info['mpeg']['audio'])) {
1403 $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1404 $info['audio'] = $getid3_temp->info['audio'];
1405 $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1406 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1407 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1408 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1409 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1410 //$info['bitrate'] = $info['audio']['bitrate'];
1411 }
1412 unset($getid3_temp, $getid3_mp3);
1413 }
1414
1415 } elseif (preg_match('/^\x0B\x77/s', $FirstFourBytes)) {
1416
1417 // AC3
1418 if (class_exists('GetId3\\Module\\Audio\\Ac3')) {
1419 $getid3_temp = new GetId3Core();
1420 $getid3_temp->openfile($this->getid3->filename);
1421 $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
1422 $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
1423 $getid3_ac3 = new Ac3($getid3_temp);
1424 $getid3_ac3->analyze();
1425 if (empty($getid3_temp->info['error'])) {
1426 $info['audio'] = $getid3_temp->info['audio'];
1427 $info['ac3'] = $getid3_temp->info['ac3'];
1428 if (!empty($getid3_temp->info['warning'])) {
1429 foreach ($getid3_temp->info['warning'] as $key => $value) {
1430 $info['warning'][] = $value;
1431 }
1432 }
1433 }
1434 unset($getid3_temp, $getid3_ac3);
1435 }
1436
1437 }
1438
1439 }
1440
1441 $FoundAllChunksWeNeed = true;
1442 fseek($this->getid3->fp, $WhereWeWere, SEEK_SET);
1443
1444 }
1445 fseek($this->getid3->fp, $chunksize - 4, SEEK_CUR);
1446
1447 //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) {
1448 //
1449 // // data chunk, ignore
1450 //
1451 } else {
1452
1453 if (!isset($RIFFchunk[$listname])) {
1454 $RIFFchunk[$listname] = array();
1455 }
1456 $LISTchunkParent = $listname;
1457 $LISTchunkMaxOffset = ftell($this->getid3->fp) - 4 + $chunksize;
1458 if ($parsedChunk = $this->ParseRIFF(ftell($this->getid3->fp), ftell($this->getid3->fp) + $chunksize - 4)) {
1459 $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1460 }
1461
1462 }
1463 break;
1464
1465 default:
1466 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1467 $nextoffset = ftell($this->getid3->fp) + $chunksize;
1468 if (($nextoffset < 0) || !Helper::intValueSupported($nextoffset)) {
1469 $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1470 break 2;
1471 }
1472 fseek($this->getid3->fp, $nextoffset, SEEK_SET);
1473 break;
1474 }
1475 $thisindex = 0;
1476 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1477 $thisindex = count($RIFFchunk[$chunkname]);
1478 }
1479 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($this->getid3->fp) - 8;
1480 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1481 switch ($chunkname) {
1482 case 'data':
1483 $info['avdataoffset'] = ftell($this->getid3->fp);
1484 $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1485
1486 $RIFFdataChunkContentsTest = fread($this->getid3->fp, 36);
1487
1488 if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
1489
1490 // Probably is MP3 data
1491 if (GetId3\Module\Audio\Mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
1492 $getid3_temp = new GetId3Core();
1493 $getid3_temp->openfile($this->getid3->filename);
1494 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1495 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
1496 $getid3_mp3 = new Mp3($getid3_temp);
1497 $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false);
1498 if (empty($getid3_temp->info['error'])) {
1499 $info['mpeg'] = $getid3_temp->info['mpeg'];
1500 $info['audio'] = $getid3_temp->info['audio'];
1501 }
1502 unset($getid3_temp, $getid3_mp3);
1503 }
1504
1505 } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == "\x0B\x77")) {
1506
1507 // This is probably AC-3 data
1508 if (class_exists('GetId3\\Module\\Audio\\Ac3')) {
1509 $getid3_temp = new GetId3Core();
1510 $getid3_temp->openfile($this->getid3->filename);
1511 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1512 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
1513 $getid3_ac3 = new Ac3($getid3_temp);
1514 $getid3_ac3->analyze();
1515 if (empty($getid3_temp->info['error'])) {
1516 $info['audio'] = $getid3_temp->info['audio'];
1517 $info['ac3'] = $getid3_temp->info['ac3'];
1518 $info['warning'] = $getid3_temp->info['warning'];
1519 }
1520 unset($getid3_temp, $getid3_ac3);
1521 }
1522
1523 } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == "\x77\x0B")) {
1524
1525 // Dolby Digital WAV
1526 // AC-3 content, but not encoded in same format as normal AC-3 file
1527 // For one thing, byte order is swapped
1528
1529 if (class_exists('GetId3\\Module\\Audio\\Ac3')) {
1530 // ok to use tmpfile here - only 56 bytes
1531 if ($RIFFtempfilename = tempnam(GetId3Core::getTempDir(), 'id3')) {
1532 if ($fd_temp = fopen($RIFFtempfilename, 'wb')) {
1533 for ($i = 0; $i < 28; $i += 2) {
1534 // swap byte order
1535 fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
1536 fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
1537 }
1538 fclose($fd_temp);
1539
1540 $getid3_temp = new GetId3Core();
1541 $getid3_temp->openfile($RIFFtempfilename);
1542 $getid3_temp->info['avdataend'] = 20;
1543 $getid3_ac3 = new Ac3($getid3_temp);
1544 $getid3_ac3->analyze();
1545 if (empty($getid3_temp->info['error'])) {
1546 $info['audio'] = $getid3_temp->info['audio'];
1547 $info['ac3'] = $getid3_temp->info['ac3'];
1548 $info['warning'] = $getid3_temp->info['warning'];
1549 } else {
1550 $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): '.implode(';', $getid3_temp->info['error']);
1551 }
1552 unset($getid3_ac3, $getid3_temp);
1553 } else {
1554 $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
1555 }
1556 unlink($RIFFtempfilename);
1557
1558 } else {
1559 $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
1560 }
1561
1562 }
1563
1564 } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) {
1565
1566 // This is WavPack data
1567 $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1568 $info['wavpack']['size'] = Helper::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
1569 $this->RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28));
1570
1571 } else {
1572
1573 // This is some other kind of data (quite possibly just PCM)
1574 // do nothing special, just skip it
1575
1576 }
1577 $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
1578 if (($nextoffset < 0) || !Helper::intValueSupported($nextoffset)) {
1579 $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1580 break 3;
1581 }
1582 fseek($this->getid3->fp, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
1583 break;
1584
1585 case 'iXML':
1586 case 'bext':
1587 case 'cart':
1588 case 'fmt ':
1589 case 'strh':
1590 case 'strf':
1591 case 'indx':
1592 case 'MEXT':
1593 case 'DISP':
1594 // always read data in
1595 case 'JUNK':
1596 // should be: never read data in
1597 // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1598 if ($chunksize < 1048576) {
1599 if ($chunksize > 0) {
1600 $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
1601 if ($chunkname == 'JUNK') {
1602 if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1603 // only keep text characters [chr(32)-chr(127)]
1604 $info['riff']['comments']['junk'][] = trim($matches[1]);
1605 }
1606 // but if nothing there, ignore
1607 // remove the key in either case
1608 unset($RIFFchunk[$chunkname][$thisindex]['data']);
1609 }
1610 }
1611 } else {
1612 $info['warning'][] = 'chunk "'.$chunkname.'" at offset '.ftell($this->getid3->fp).' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data';
1613 $nextoffset = ftell($this->getid3->fp) + $chunksize;
1614 if (($nextoffset < 0) || !Helper::intValueSupported($nextoffset)) {
1615 $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1616 break 3;
1617 }
1618 fseek($this->getid3->fp, $nextoffset, SEEK_SET);
1619 }
1620 break;
1621
1622 default:
1623 if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1624 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1625 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1626 unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1627 unset($RIFFchunk[$chunkname][$thisindex]['size']);
1628 if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1629 unset($RIFFchunk[$chunkname][$thisindex]);
1630 }
1631 if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1632 unset($RIFFchunk[$chunkname]);
1633 }
1634 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
1635 //} elseif (in_array($chunkname, array('ID3 ')) || (($chunksize > 0) && ($chunksize < 2048))) {
1636 } elseif (($chunksize > 0) && ($chunksize < 2048)) {
1637 // only read data in if smaller than 2kB
1638 $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
1639 } else {
1640 $nextoffset = ftell($this->getid3->fp) + $chunksize;
1641 if (($nextoffset < 0) || !Helper::intValueSupported($nextoffset)) {
1642 $info['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
1643 break 3;
1644 }
1645 fseek($this->getid3->fp, $nextoffset, SEEK_SET);
1646 }
1647 break;
1648 }
1649 break;
1650
1651 }
1652
1653 }
1654
1655 return $RIFFchunk;
1656 }
1657
1663 public function ParseRIFFdata(&$RIFFdata)
1664 {
1665 $info = &$this->getid3->info;
1666 if ($RIFFdata) {
1667 $tempfile = tempnam(GetId3Core::getTempDir(), 'getID3');
1668 $fp_temp = fopen($tempfile, 'wb');
1669 $RIFFdataLength = strlen($RIFFdata);
1670 $NewLengthString = Helper::LittleEndian2String($RIFFdataLength, 4);
1671 for ($i = 0; $i < 4; $i++) {
1672 $RIFFdata{$i + 4} = $NewLengthString{$i};
1673 }
1674 fwrite($fp_temp, $RIFFdata);
1675 fclose($fp_temp);
1676
1677 $getid3_temp = new GetId3Core();
1678 $getid3_temp->openfile($tempfile);
1679 $getid3_temp->info['filesize'] = $RIFFdataLength;
1680 $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1681 $getid3_temp->info['tags'] = $info['tags'];
1682 $getid3_temp->info['warning'] = $info['warning'];
1683 $getid3_temp->info['error'] = $info['error'];
1684 $getid3_temp->info['comments'] = $info['comments'];
1685 $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1686 $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1687 $getid3_riff = new self($getid3_temp);
1688 $getid3_riff->analyze();
1689
1690 $info['riff'] = $getid3_temp->info['riff'];
1691 $info['warning'] = $getid3_temp->info['warning'];
1692 $info['error'] = $getid3_temp->info['error'];
1693 $info['tags'] = $getid3_temp->info['tags'];
1694 $info['comments'] = $getid3_temp->info['comments'];
1695 unset($getid3_riff, $getid3_temp);
1696 unlink($tempfile);
1697 }
1698
1699 return false;
1700 }
1701
1707 public static function RIFFparseWAVEFORMATex($WaveFormatExData)
1708 {
1709 // shortcut
1710 $WaveFormatEx['raw'] = array();
1711 $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1712
1713 $WaveFormatEx_raw['wFormatTag'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 0, 2));
1714 $WaveFormatEx_raw['nChannels'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 2, 2));
1715 $WaveFormatEx_raw['nSamplesPerSec'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 4, 4));
1716 $WaveFormatEx_raw['nAvgBytesPerSec'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 8, 4));
1717 $WaveFormatEx_raw['nBlockAlign'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 12, 2));
1718 $WaveFormatEx_raw['wBitsPerSample'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 14, 2));
1719 if (strlen($WaveFormatExData) > 16) {
1720 $WaveFormatEx_raw['cbSize'] = Helper::LittleEndian2Int(substr($WaveFormatExData, 16, 2));
1721 }
1722
1723 $WaveFormatEx['codec'] = self::RIFFwFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1724 $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1725 $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1726 $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1727 $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1728
1729 return $WaveFormatEx;
1730 }
1731
1737 public function RIFFparseWavPackHeader($WavPackChunkData)
1738 {
1739 // typedef struct {
1740 // char ckID [4];
1741 // long ckSize;
1742 // short version;
1743 // short bits; // added for version 2.00
1744 // short flags, shift; // added for version 3.00
1745 // long total_samples, crc, crc2;
1746 // char extension [4], extra_bc, extras [3];
1747 // } WavpackHeader;
1748
1749 // shortcut
1750 $info = &$this->getid3->info;
1751 $info['wavpack'] = array();
1752 $thisfile_wavpack = &$info['wavpack'];
1753
1754 $thisfile_wavpack['version'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1755 if ($thisfile_wavpack['version'] >= 2) {
1756 $thisfile_wavpack['bits'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1757 }
1758 if ($thisfile_wavpack['version'] >= 3) {
1759 $thisfile_wavpack['flags_raw'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1760 $thisfile_wavpack['shift'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1761 $thisfile_wavpack['total_samples'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1762 $thisfile_wavpack['crc1'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1763 $thisfile_wavpack['crc2'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1764 $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1765 $thisfile_wavpack['extra_bc'] = Helper::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1766 for ($i = 0; $i <= 2; $i++) {
1767 $thisfile_wavpack['extras'][] = Helper::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1768 }
1769
1770 // shortcut
1771 $thisfile_wavpack['flags'] = array();
1772 $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1773
1774 $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1775 $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1776 $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1777 $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1778 $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1779 $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1780 $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1781 $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1782 $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1783 $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1784 $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1785 $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1786 $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1787 $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1788 $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1789 $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1790 $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1791 $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1792 $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1793 $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1794 }
1795
1796 return true;
1797 }
1798
1805 public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true)
1806 {
1807 $functionname = ($littleEndian ? 'LittleEndian2Int' : 'BigEndian2Int');
1808 $parsed['biSize'] = Helper::$functionname(substr($BITMAPINFOHEADER, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure
1809 $parsed['biWidth'] = Helper::$functionname(substr($BITMAPINFOHEADER, 4, 4)); // width of the bitmap in pixels
1810 $parsed['biHeight'] = Helper::$functionname(substr($BITMAPINFOHEADER, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
1811 $parsed['biPlanes'] = Helper::$functionname(substr($BITMAPINFOHEADER, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1
1812 $parsed['biBitCount'] = Helper::$functionname(substr($BITMAPINFOHEADER, 14, 2)); // Specifies the number of bits per pixels
1813 $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1814 $parsed['biSizeImage'] = Helper::$functionname(substr($BITMAPINFOHEADER, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1815 $parsed['biXPelsPerMeter'] = Helper::$functionname(substr($BITMAPINFOHEADER, 24, 4)); // horizontal resolution, in pixels per metre, of the target device
1816 $parsed['biYPelsPerMeter'] = Helper::$functionname(substr($BITMAPINFOHEADER, 28, 4)); // vertical resolution, in pixels per metre, of the target device
1817 $parsed['biClrUsed'] = Helper::$functionname(substr($BITMAPINFOHEADER, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
1818 $parsed['biClrImportant'] = Helper::$functionname(substr($BITMAPINFOHEADER, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
1819
1820 return $parsed;
1821 }
1822
1830 public static function ParseDIVXTAG($DIVXTAG)
1831 {
1832 // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1833 // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1834 // 'Byte Layout: '1111111111111111
1835 // '32 for Movie - 1 '1111111111111111
1836 // '28 for Author - 6 '6666666666666666
1837 // '4 for year - 2 '6666666666662222
1838 // '3 for genre - 3 '7777777777777777
1839 // '48 for Comments - 7 '7777777777777777
1840 // '1 for Rating - 4 '7777777777777777
1841 // '5 for Future Additions - 0 '333400000DIVXTAG
1842 // '128 bytes total
1843
1844 static $DIVXTAGgenre = array(
1845 0 => 'Action',
1846 1 => 'Action/Adventure',
1847 2 => 'Adventure',
1848 3 => 'Adult',
1849 4 => 'Anime',
1850 5 => 'Cartoon',
1851 6 => 'Claymation',
1852 7 => 'Comedy',
1853 8 => 'Commercial',
1854 9 => 'Documentary',
1855 10 => 'Drama',
1856 11 => 'Home Video',
1857 12 => 'Horror',
1858 13 => 'Infomercial',
1859 14 => 'Interactive',
1860 15 => 'Mystery',
1861 16 => 'Music Video',
1862 17 => 'Other',
1863 18 => 'Religion',
1864 19 => 'Sci Fi',
1865 20 => 'Thriller',
1866 21 => 'Western',
1867 );
1868 static $DIVXTAGrating = array(
1869 0=>'Unrated',
1870 1=>'G',
1871 2=>'PG',
1872 3=>'PG-13',
1873 4=>'R',
1874 5=>'NC-17'
1875 );
1876
1877 $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1878 $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1879 $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1880 $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1881 $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1882 $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1883 //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1884 //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1885
1886 $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1887 $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1888
1889 return $parsed;
1890 }
1891
1897 public static function RIFFwaveSNDMtagLookup($tagshortname)
1898 {
1899 $begin = __LINE__;
1900
1918 return Helper::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
1919 }
1920
1926 public static function RIFFwFormatTagLookup($wFormatTag)
1927 {
1928 $begin = __LINE__;
1929
2092 return Helper::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2093
2094 }
2095
2101 public static function RIFFfourccLookup($fourcc)
2102 {
2103 $begin = __LINE__;
2104
2492 return Helper::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
2493 }
2494
2501 public function EitherEndian2Int($byteword, $signed=false)
2502 {
2503 if ($this->getid3->info['fileformat'] == 'riff') {
2504 return Helper::LittleEndian2Int($byteword, $signed);
2505 }
2506
2507 return Helper::BigEndian2Int($byteword, false, $signed);
2508 }
2509
2510}
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
GetId3() by James Heinrich info@getid3.org //.
Definition: GetId3Core.php:26
GetId3() by James Heinrich info@getid3.org //.
Definition: BaseHandler.php:26
fseek($bytes, $whence=SEEK_SET)
GetId3() by James Heinrich info@getid3.org //.
Definition: Helper.php:27
static Bin2Dec($binstring, $signed=false)
Definition: Helper.php:496
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
static FixedPoint16_16($rawdata)
Definition: Helper.php:696
static RGADoriginatorLookup($originatorcode)
@staticvar array $RGADoriginator
Definition: Helper.php:1552
static LittleEndian2Float($byteword)
Definition: Helper.php:277
static intValueSupported($num)
@staticvar null $hasINT64
Definition: Helper.php:130
static LittleEndian2String($number, $minbytes=1, $synchsafe=false)
Definition: Helper.php:538
static EmbeddedLookup($key, $begin, $end, $file, $name)
@staticvar type $cache
Definition: Helper.php:1759
static XML2array($XMLstring)
Definition: Helper.php:798
static array_merge_noclobber($array1, $array2)
Definition: Helper.php:587
static BigEndian2Float($byteword)
ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic.
Definition: Helper.php:290
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
static RGADadjustmentLookup($rawadjustment, $signbit)
Definition: Helper.php:1571
static Dec2Bin($number)
Definition: Helper.php:472
static CastAsInt($floatnum)
Definition: Helper.php:107
static RGADnameLookup($namecode)
@staticvar array $RGADname
Definition: Helper.php:1534
static DateMac2Unix($macdate)
Definition: Helper.php:671
GetId3() by James Heinrich info@getid3.org //.
Definition: Mpeg.php:32
GetId3() by James Heinrich info@getid3.org //.
Definition: Riff.php:43
static RIFFfourccLookup($fourcc)
Definition: Riff.php:2101
ParseRIFF($startoffset, $maxoffset)
Definition: Riff.php:1331
static ParseDIVXTAG($DIVXTAG)
@staticvar array $DIVXTAGgenre @staticvar array $DIVXTAGrating
Definition: Riff.php:1830
static ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true)
Definition: Riff.php:1805
EitherEndian2Int($byteword, $signed=false)
Definition: Riff.php:2501
RIFFparseWavPackHeader($WavPackChunkData)
Definition: Riff.php:1737
static RIFFwaveSNDMtagLookup($tagshortname)
Definition: Riff.php:1897
static RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray)
Definition: Riff.php:1265
static RIFFparseWAVEFORMATex($WaveFormatExData)
Definition: Riff.php:1707
static RIFFwFormatTagLookup($wFormatTag)
Definition: Riff.php:1926
GetId3() by James Heinrich info@getid3.org //.
Definition: Ac3.php:30
GetId3() by James Heinrich info@getid3.org //.
Definition: Mp3.php:39
static MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false)
Definition: Mp3.php:1930
GetId3() by James Heinrich info@getid3.org //.
Definition: Id3v2.php:31
$h
$info
Definition: example_052.php:80