2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org> //
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 /////////////////////////////////////////////////////////////////
7 // See readme.txt for more details //
8 /////////////////////////////////////////////////////////////////
10 // module.audio-video.riff.php //
11 // module for analyzing RIFF files //
12 // multiple formats supported by this module: //
13 // Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX //
14 // dependencies: module.audio.mp3.php //
15 // module.audio.ac3.php //
16 // module.audio.dts.php //
18 /////////////////////////////////////////////////////////////////
21 * @todo Parse AC-3/DTS audio inside WAVE correctly
22 * @todo Rewrite RIFF parser totally
25 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
26 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
27 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29 class getid3_riff extends getid3_handler
32 public function Analyze() {
33 $info = &$this->getid3->info;
35 // initialize these values to an empty array, otherwise they default to NULL
36 // and you can't append array values to a NULL value
37 $info['riff'] = array('raw'=>array());
40 $thisfile_riff = &$info['riff'];
41 $thisfile_riff_raw = &$thisfile_riff['raw'];
42 $thisfile_audio = &$info['audio'];
43 $thisfile_video = &$info['video'];
44 $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
45 $thisfile_riff_audio = &$thisfile_riff['audio'];
46 $thisfile_riff_video = &$thisfile_riff['video'];
48 $Original['avdataoffset'] = $info['avdataoffset'];
49 $Original['avdataend'] = $info['avdataend'];
51 $this->fseek($info['avdataoffset']);
52 $RIFFheader = $this->fread(12);
53 $offset = $this->ftell();
54 $RIFFtype = substr($RIFFheader, 0, 4);
55 $RIFFsize = substr($RIFFheader, 4, 4);
56 $RIFFsubtype = substr($RIFFheader, 8, 4);
60 case 'FORM': // AIFF, AIFC
61 $info['fileformat'] = 'aiff';
62 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
63 $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
66 case 'RIFF': // AVI, WAV, etc
67 case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
68 case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
69 $info['fileformat'] = 'riff';
70 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
71 if ($RIFFsubtype == 'RMP3') {
72 // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
73 $RIFFsubtype = 'WAVE';
75 $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
76 if (($info['avdataend'] - $info['filesize']) == 1) {
77 // LiteWave appears to incorrectly *not* pad actual output file
78 // to nearest WORD boundary so may appear to be short by one
79 // byte, in which case - skip warning
80 $info['avdataend'] = $info['filesize'];
83 $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
84 while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
86 $this->fseek($nextRIFFoffset);
87 } catch (getid3_exception $e) {
88 if ($e->getCode() == 10) {
89 //$this->warning('RIFF parser: '.$e->getMessage());
90 $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
91 $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
97 $nextRIFFheader = $this->fread(12);
98 if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
99 if (substr($nextRIFFheader, 0, 1) == "\x00") {
100 // RIFF padded to WORD boundary, we're actually already at the end
104 $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
105 $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
106 $nextRIFFtype = substr($nextRIFFheader, 8, 4);
107 $chunkdata = array();
108 $chunkdata['offset'] = $nextRIFFoffset + 8;
109 $chunkdata['size'] = $nextRIFFsize;
110 $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
112 switch ($nextRIFFheaderID) {
115 $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
117 if (!isset($thisfile_riff[$nextRIFFtype])) {
118 $thisfile_riff[$nextRIFFtype] = array();
120 $thisfile_riff[$nextRIFFtype][] = $chunkdata;
125 $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
129 $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
133 if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
134 $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
135 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
136 // 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
137 $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
138 $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
142 $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
148 if ($RIFFsubtype == 'WAVE') {
149 $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
154 $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
155 unset($info['fileformat']);
160 switch ($RIFFsubtype) {
162 if (empty($thisfile_audio['bitrate_mode'])) {
163 $thisfile_audio['bitrate_mode'] = 'cbr';
165 if (empty($thisfile_audio_dataformat)) {
166 $thisfile_audio_dataformat = 'wav';
169 if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
170 $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
171 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
173 if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
175 $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
176 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
177 if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
178 $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
181 $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
182 unset($thisfile_riff_audio[$streamindex]['raw']);
183 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
185 $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
186 if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
187 $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
189 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
191 if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
192 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
195 $thisfile_audio['lossless'] = false;
196 if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
197 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
200 $thisfile_audio['lossless'] = true;
204 $thisfile_audio_dataformat = 'ac3';
213 $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
214 $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
215 $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
216 $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
219 if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
222 $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
223 $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array());
224 $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad'];
225 $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
226 $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
228 $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
229 $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
230 $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
232 $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
233 $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
234 $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
235 $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
236 $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
237 $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
238 $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
239 $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
240 $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
241 $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
243 $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
244 if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
245 $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
246 $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
247 $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
249 if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
250 $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
251 $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
252 $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
256 if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
257 $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
259 // This should be a good way of calculating exact playtime,
260 // but some sample files have had incorrect number of samples,
261 // so cannot use this method
263 // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
264 // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
267 if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
268 $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
271 if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
273 $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
275 $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
276 $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
277 $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
278 $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
279 $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
280 $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
281 $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
282 $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
283 $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
284 if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
285 if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
286 list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
287 list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
288 $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']);
290 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
293 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
295 $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
296 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
299 if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
301 $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
303 $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
304 $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
305 if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
306 $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
307 $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
308 $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
310 $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
312 $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
313 $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
314 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
315 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
316 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
319 if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
321 $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
323 $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
324 $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
325 $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
326 $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
327 $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
328 $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
329 $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
330 $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
331 $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
332 $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
333 $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
334 $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
335 $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
336 $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
337 $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
338 $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
339 for ($i = 0; $i < 8; $i++) {
340 $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
341 $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
343 $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
344 $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
346 $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
347 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
350 if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
351 // SoundMiner metadata
354 $thisfile_riff_WAVE_SNDM_0 = &$thisfile_riff_WAVE['SNDM'][0];
355 $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
356 $SNDM_startoffset = 0;
357 $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
359 while ($SNDM_startoffset < $SNDM_endoffset) {
360 $SNDM_thisTagOffset = 0;
361 $SNDM_thisTagSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
362 $SNDM_thisTagOffset += 4;
363 $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
364 $SNDM_thisTagOffset += 4;
365 $SNDM_thisTagDataSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
366 $SNDM_thisTagOffset += 2;
367 $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
368 $SNDM_thisTagOffset += 2;
369 $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
370 $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
372 if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
373 $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).')';
375 } elseif ($SNDM_thisTagSize <= 0) {
376 $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
379 $SNDM_startoffset += $SNDM_thisTagSize;
381 $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
382 if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
383 $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
385 $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
390 'tracktitle'=>'title',
391 'category' =>'genre',
393 'tracktitle'=>'title',
395 foreach ($tagmapping as $fromkey => $tokey) {
396 if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
397 $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
402 if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
403 // requires functions simplexml_load_string and get_object_vars
404 if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
405 $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
406 if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
407 @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
408 $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
410 if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
411 @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
412 $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
414 if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
415 $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
416 $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
417 $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
418 $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
419 $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
420 $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
421 $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
422 $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
430 if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
431 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
432 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
435 if (!empty($info['wavpack'])) {
436 $thisfile_audio_dataformat = 'wavpack';
437 $thisfile_audio['bitrate_mode'] = 'vbr';
438 $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
440 // Reset to the way it was - RIFF parsing will have messed this up
441 $info['avdataend'] = $Original['avdataend'];
442 $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
444 $this->fseek($info['avdataoffset'] - 44);
445 $RIFFdata = $this->fread(44);
446 $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
447 $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
449 if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
450 $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
451 $this->fseek($info['avdataend']);
452 $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
455 // move the data chunk after all other chunks (if any)
456 // so that the RIFF parser doesn't see EOF when trying
457 // to skip over the data chunk
458 $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
459 $getid3_riff = new getid3_riff($this->getid3);
460 $getid3_riff->ParseRIFFdata($RIFFdata);
464 if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
465 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
467 if (!empty($info['ac3'])) {
468 // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
469 $thisfile_audio['wformattag'] = 0x2000;
470 $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
471 $thisfile_audio['lossless'] = false;
472 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
473 $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
475 if (!empty($info['dts'])) {
476 // Dolby DTS files masquerade as PCM-WAV, but they're not
477 $thisfile_audio['wformattag'] = 0x2001;
478 $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
479 $thisfile_audio['lossless'] = false;
480 $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
481 $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
484 case 0x08AE: // ClearJump LiteWave
485 $thisfile_audio['bitrate_mode'] = 'vbr';
486 $thisfile_audio_dataformat = 'litewave';
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
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
501 // PCMWAVEFORMAT m_OrgWf; // original wave format
502 // }SLwFormat, *PSLwFormat;
505 $thisfile_riff['litewave']['raw'] = array();
506 $riff_litewave = &$thisfile_riff['litewave'];
507 $riff_litewave_raw = &$riff_litewave['raw'];
510 'compression_method' => 1,
511 'compression_flags' => 1,
513 'm_dwBlockSize' => 4,
515 'm_wMarkDistance' => 2,
518 'm_bFactExists' => 2,
519 'm_dwRiffChunkSize' => 4,
521 $litewave_offset = 18;
522 foreach ($flags as $flag => $length) {
523 $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
524 $litewave_offset += $length;
527 //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
528 $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
530 $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
531 $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
532 $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
534 $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
535 $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
542 if ($info['avdataend'] > $info['filesize']) {
543 switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
544 case 'wavpack': // WavPack
546 case 'ofr': // OptimFROG
547 case 'ofs': // OptimFROG DualStream
548 // lossless compressed audio formats that keep original RIFF headers - skip warning
552 if (($info['avdataend'] - $info['filesize']) == 1) {
553 // LiteWave appears to incorrectly *not* pad actual output file
554 // to nearest WORD boundary so may appear to be short by one
555 // byte, in which case - skip warning
557 // Short by more than one byte, throw warning
558 $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)';
559 $info['avdataend'] = $info['filesize'];
564 if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
565 // output file appears to be incorrectly *not* padded to nearest WORD boundary
566 // Output less severe warning
567 $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)';
568 $info['avdataend'] = $info['filesize'];
570 // Short by more than one byte, throw warning
571 $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)';
572 $info['avdataend'] = $info['filesize'];
577 if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
578 if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
579 $info['avdataend']--;
580 $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
583 if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
584 unset($thisfile_audio['bits_per_sample']);
585 if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
586 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
592 $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
593 $thisfile_video['dataformat'] = 'avi';
594 $info['mime_type'] = 'video/avi';
596 if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
597 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
598 if (isset($thisfile_riff['AVIX'])) {
599 $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
601 $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
603 if ($info['avdataend'] > $info['filesize']) {
604 $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)';
605 $info['avdataend'] = $info['filesize'];
609 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
610 //$bIndexType = array(
611 // 0x00 => 'AVI_INDEX_OF_INDEXES',
612 // 0x01 => 'AVI_INDEX_OF_CHUNKS',
613 // 0x80 => 'AVI_INDEX_IS_DATA',
615 //$bIndexSubtype = array(
617 // 0x01 => 'AVI_INDEX_2FIELD',
620 foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
621 $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
623 $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
624 $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
625 $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
626 $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
627 $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
628 $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
630 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
631 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
636 if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
637 $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
640 $thisfile_riff_raw['avih'] = array();
641 $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
643 $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
644 if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
645 $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
650 'dwMaxBytesPerSec', // max. transfer rate
651 'dwPaddingGranularity', // pad to multiples of this size; normally 2K.
652 'dwFlags', // the ever-present flags
653 'dwTotalFrames', // # frames in file
654 'dwInitialFrames', //
656 'dwSuggestedBufferSize', //
665 foreach ($flags as $flag) {
666 $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
671 'hasindex' => 0x00000010,
672 'mustuseindex' => 0x00000020,
673 'interleaved' => 0x00000100,
674 'trustcktype' => 0x00000800,
675 'capturedfile' => 0x00010000,
676 'copyrighted' => 0x00020010,
678 foreach ($flags as $flag => $value) {
679 $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
683 $thisfile_riff_video[$streamindex] = array();
684 $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
686 if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
687 $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
688 $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
690 if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
691 $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
692 $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
694 if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
695 $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
696 $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
699 $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
700 $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
702 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
703 if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
704 for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
705 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
706 $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
707 $strhfccType = substr($strhData, 0, 4);
709 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
710 $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
713 $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
715 switch ($strhfccType) {
717 $thisfile_audio['bitrate_mode'] = 'cbr';
718 $thisfile_audio_dataformat = 'wav';
719 if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
720 $streamindex = count($thisfile_riff_audio);
723 $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
724 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
727 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
728 $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
730 if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
731 unset($thisfile_audio_streams_currentstream['bits_per_sample']);
733 $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
734 unset($thisfile_audio_streams_currentstream['raw']);
737 $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
739 unset($thisfile_riff_audio[$streamindex]['raw']);
740 $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
742 $thisfile_audio['lossless'] = false;
743 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
745 $thisfile_audio_dataformat = 'wav';
746 $thisfile_audio['lossless'] = true;
749 case 0x0050: // MPEG Layer 2 or Layer 1
750 $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
753 case 0x0055: // MPEG Layer 3
754 $thisfile_audio_dataformat = 'mp3';
758 $thisfile_audio_dataformat = 'aac';
761 case 0x0161: // Windows Media v7 / v8 / v9
762 case 0x0162: // Windows Media Professional v9
763 case 0x0163: // Windows Media Lossess v9
764 $thisfile_audio_dataformat = 'wma';
768 $thisfile_audio_dataformat = 'ac3';
772 $thisfile_audio_dataformat = 'dts';
776 $thisfile_audio_dataformat = 'wav';
779 $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
780 $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
781 $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
788 $thisfile_riff_raw['strh'][$i] = array();
789 $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
791 $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
792 $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
793 $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
794 $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
795 $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
796 $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
797 $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
798 $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
799 $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
800 $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
801 $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
802 $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
803 $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
804 $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
806 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
807 $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
808 if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
809 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
810 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
812 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
813 $thisfile_video['pixel_aspect_ratio'] = (float) 1;
814 switch ($thisfile_riff_raw_strh_current['fccHandler']) {
815 case 'HFYU': // Huffman Lossless Codec
816 case 'IRAW': // Intel YUV Uncompressed
817 case 'YUY2': // Uncompressed YUV 4:2:2
818 $thisfile_video['lossless'] = true;
822 $thisfile_video['lossless'] = false;
826 switch ($strhfccType) {
828 $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($info['fileformat'] == 'riff'));
829 $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
831 if ($thisfile_riff_video_current['codec'] == 'DV') {
832 $thisfile_riff_video_current['dv_type'] = 2;
837 $thisfile_riff_video_current['dv_type'] = 1;
843 $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
850 if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
852 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
853 if (self::fourccLookup($thisfile_video['fourcc'])) {
854 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
855 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
858 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
859 case 'HFYU': // Huffman Lossless Codec
860 case 'IRAW': // Intel YUV Uncompressed
861 case 'YUY2': // Uncompressed YUV 4:2:2
862 $thisfile_video['lossless'] = true;
863 //$thisfile_video['bits_per_sample'] = 24;
867 $thisfile_video['lossless'] = false;
868 //$thisfile_video['bits_per_sample'] = 24;
879 $thisfile_audio['bitrate_mode'] = 'cbr';
880 $thisfile_audio_dataformat = 'cda';
881 $thisfile_audio['lossless'] = true;
882 unset($info['mime_type']);
884 $info['avdataoffset'] = 44;
886 if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
888 $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
890 $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
891 $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
892 $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
893 $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
894 $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
895 $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
896 $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
898 $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
899 $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
900 $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
901 $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
903 // hardcoded data for CD-audio
904 $thisfile_audio['sample_rate'] = 44100;
905 $thisfile_audio['channels'] = 2;
906 $thisfile_audio['bits_per_sample'] = 16;
907 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
908 $thisfile_audio['bitrate_mode'] = 'cbr';
915 $thisfile_audio['bitrate_mode'] = 'cbr';
916 $thisfile_audio_dataformat = 'aiff';
917 $thisfile_audio['lossless'] = true;
918 $info['mime_type'] = 'audio/x-aiff';
920 if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
921 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
922 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
923 if ($info['avdataend'] > $info['filesize']) {
924 if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
925 // structures rounded to 2-byte boundary, but dumb encoders
926 // forget to pad end of file to make this actually work
928 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
930 $info['avdataend'] = $info['filesize'];
934 if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
937 $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
939 $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
940 $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
941 $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
942 $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
944 if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
945 $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
946 $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
947 $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
948 switch ($thisfile_riff_audio['codec_name']) {
950 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
951 $thisfile_audio['lossless'] = true;
955 switch ($thisfile_riff_audio['codec_fourcc']) {
956 // http://developer.apple.com/qa/snd/snd07.html
958 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
959 $thisfile_audio['lossless'] = true;
963 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
964 $thisfile_audio['lossless'] = true;
973 $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
974 $thisfile_audio['lossless'] = false;
979 $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
980 if ($thisfile_riff_audio['bits_per_sample'] > 0) {
981 $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
983 $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
984 if ($thisfile_audio['sample_rate'] == 0) {
985 $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
988 $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
991 if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
993 $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
995 for ($i = 0; $i < $CommentCount; $i++) {
996 $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
998 $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1000 $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1002 $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1003 $offset += $CommentLength;
1005 $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1006 $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1010 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1011 foreach ($CommentsChunkNames as $key => $value) {
1012 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1013 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1017 if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1018 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1019 $getid3_temp = new getID3();
1020 $getid3_temp->openfile($this->getid3->filename);
1021 $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1022 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1023 if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1024 $info['id3v2'] = $getid3_temp->info['id3v2'];
1026 unset($getid3_temp, $getid3_id3v2);
1032 $thisfile_audio['bitrate_mode'] = 'cbr';
1033 $thisfile_audio_dataformat = '8svx';
1034 $thisfile_audio['bits_per_sample'] = 8;
1035 $thisfile_audio['channels'] = 1; // overridden below, if need be
1036 $info['mime_type'] = 'audio/x-aiff';
1038 if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1039 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1040 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1041 if ($info['avdataend'] > $info['filesize']) {
1042 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1046 if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1048 $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1050 $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1051 $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1052 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1053 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1054 $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1055 $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1056 $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1058 $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1060 switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1062 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1063 $thisfile_audio['lossless'] = true;
1064 $ActualBitsPerSample = 8;
1068 $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1069 $thisfile_audio['lossless'] = false;
1070 $ActualBitsPerSample = 4;
1074 $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1079 if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1080 $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1081 switch ($ChannelsIndex) {
1083 $thisfile_audio['channels'] = 2;
1086 case 2: // Left channel only
1087 case 4: // Right channel only
1088 $thisfile_audio['channels'] = 1;
1092 $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1098 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1099 foreach ($CommentsChunkNames as $key => $value) {
1100 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1101 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1105 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1106 if (!empty($thisfile_audio['bitrate'])) {
1107 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1113 $info['mime_type'] = 'video/mpeg';
1114 if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1115 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) {
1116 $getid3_temp = new getID3();
1117 $getid3_temp->openfile($this->getid3->filename);
1118 $getid3_mpeg = new getid3_mpeg($getid3_temp);
1119 $getid3_mpeg->Analyze();
1120 if (empty($getid3_temp->info['error'])) {
1121 $info['audio'] = $getid3_temp->info['audio'];
1122 $info['video'] = $getid3_temp->info['video'];
1123 $info['mpeg'] = $getid3_temp->info['mpeg'];
1124 $info['warning'] = $getid3_temp->info['warning'];
1126 unset($getid3_temp, $getid3_mpeg);
1133 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1134 unset($info['fileformat']);
1138 switch ($RIFFsubtype) {
1142 $ID3v2_key_good = 'id3 ';
1143 $ID3v2_keys_bad = array('ID3 ', 'tag ');
1144 foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1145 if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1146 $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1147 $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1151 if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1152 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1153 $getid3_temp = new getID3();
1154 $getid3_temp->openfile($this->getid3->filename);
1155 $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1156 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1157 if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1158 $info['id3v2'] = $getid3_temp->info['id3v2'];
1160 unset($getid3_temp, $getid3_id3v2);
1165 if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1166 $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1168 if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1169 self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1171 if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1172 self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1175 if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1176 $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1179 if (!isset($info['playtime_seconds'])) {
1180 $info['playtime_seconds'] = 0;
1182 if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1183 // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1184 $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1185 } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1186 $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1189 if ($info['playtime_seconds'] > 0) {
1190 if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1192 if (!isset($info['bitrate'])) {
1193 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1196 } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1198 if (!isset($thisfile_audio['bitrate'])) {
1199 $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1202 } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1204 if (!isset($thisfile_video['bitrate'])) {
1205 $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1212 if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1214 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1215 $thisfile_audio['bitrate'] = 0;
1216 $thisfile_video['bitrate'] = $info['bitrate'];
1217 foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1218 $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1219 $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1221 if ($thisfile_video['bitrate'] <= 0) {
1222 unset($thisfile_video['bitrate']);
1224 if ($thisfile_audio['bitrate'] <= 0) {
1225 unset($thisfile_audio['bitrate']);
1229 if (isset($info['mpeg']['audio'])) {
1230 $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1231 $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1232 $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1233 $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1234 $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1235 if (!empty($info['mpeg']['audio']['codec'])) {
1236 $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1238 if (!empty($thisfile_audio['streams'])) {
1239 foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1240 if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1241 $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1242 $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1243 $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1244 $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1245 $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1249 $getid3_mp3 = new getid3_mp3($this->getid3);
1250 $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1255 if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1256 switch ($thisfile_audio_dataformat) {
1258 // ignore bits_per_sample
1262 $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1268 if (empty($thisfile_riff_raw)) {
1269 unset($thisfile_riff['raw']);
1271 if (empty($thisfile_riff_audio)) {
1272 unset($thisfile_riff['audio']);
1274 if (empty($thisfile_riff_video)) {
1275 unset($thisfile_riff['video']);
1281 public function ParseRIFF($startoffset, $maxoffset) {
1282 $info = &$this->getid3->info;
1285 $FoundAllChunksWeNeed = false;
1288 $this->fseek($startoffset);
1289 $maxoffset = min($maxoffset, $info['avdataend']);
1290 while ($this->ftell() < $maxoffset) {
1291 $chunknamesize = $this->fread(8);
1292 //$chunkname = substr($chunknamesize, 0, 4);
1293 $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
1294 $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1295 //if (strlen(trim($chunkname, "\x00")) < 4) {
1296 if (strlen($chunkname) < 4) {
1297 $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1300 if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1301 $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1304 if (($chunksize % 2) != 0) {
1305 // all structures are packed on word boundaries
1309 switch ($chunkname) {
1311 $listname = $this->fread(4);
1312 if (preg_match('#^(movi|rec )$#i', $listname)) {
1313 $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1314 $RIFFchunk[$listname]['size'] = $chunksize;
1316 if (!$FoundAllChunksWeNeed) {
1317 $WhereWeWere = $this->ftell();
1318 $AudioChunkHeader = $this->fread(12);
1319 $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1320 $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1321 $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1323 if ($AudioChunkStreamType == 'wb') {
1324 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1325 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1327 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1328 $getid3_temp = new getID3();
1329 $getid3_temp->openfile($this->getid3->filename);
1330 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1331 $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1332 $getid3_mp3 = new getid3_mp3($getid3_temp);
1333 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1334 if (isset($getid3_temp->info['mpeg']['audio'])) {
1335 $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1336 $info['audio'] = $getid3_temp->info['audio'];
1337 $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1338 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1339 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1340 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1341 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1342 //$info['bitrate'] = $info['audio']['bitrate'];
1344 unset($getid3_temp, $getid3_mp3);
1347 } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1350 $getid3_temp = new getID3();
1351 $getid3_temp->openfile($this->getid3->filename);
1352 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1353 $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1354 $getid3_ac3 = new getid3_ac3($getid3_temp);
1355 $getid3_ac3->Analyze();
1356 if (empty($getid3_temp->info['error'])) {
1357 $info['audio'] = $getid3_temp->info['audio'];
1358 $info['ac3'] = $getid3_temp->info['ac3'];
1359 if (!empty($getid3_temp->info['warning'])) {
1360 foreach ($getid3_temp->info['warning'] as $key => $value) {
1361 $info['warning'][] = $value;
1365 unset($getid3_temp, $getid3_ac3);
1368 $FoundAllChunksWeNeed = true;
1369 $this->fseek($WhereWeWere);
1371 $this->fseek($chunksize - 4, SEEK_CUR);
1375 if (!isset($RIFFchunk[$listname])) {
1376 $RIFFchunk[$listname] = array();
1378 $LISTchunkParent = $listname;
1379 $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1380 if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1381 $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1388 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1389 $this->fseek($chunksize, SEEK_CUR);
1393 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1394 $thisindex = count($RIFFchunk[$chunkname]);
1396 $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1397 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1398 switch ($chunkname) {
1400 $info['avdataoffset'] = $this->ftell();
1401 $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1403 $testData = $this->fread(36);
1404 if ($testData === '') {
1407 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1409 // Probably is MP3 data
1410 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1411 $getid3_temp = new getID3();
1412 $getid3_temp->openfile($this->getid3->filename);
1413 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1414 $getid3_temp->info['avdataend'] = $info['avdataend'];
1415 $getid3_mp3 = new getid3_mp3($getid3_temp);
1416 $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1417 if (empty($getid3_temp->info['error'])) {
1418 $info['audio'] = $getid3_temp->info['audio'];
1419 $info['mpeg'] = $getid3_temp->info['mpeg'];
1421 unset($getid3_temp, $getid3_mp3);
1424 } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1426 // This is probably AC-3 data
1427 $getid3_temp = new getID3();
1428 if ($isRegularAC3) {
1429 $getid3_temp->openfile($this->getid3->filename);
1430 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1431 $getid3_temp->info['avdataend'] = $info['avdataend'];
1433 $getid3_ac3 = new getid3_ac3($getid3_temp);
1434 if ($isRegularAC3) {
1435 $getid3_ac3->Analyze();
1437 // Dolby Digital WAV
1438 // AC-3 content, but not encoded in same format as normal AC-3 file
1439 // For one thing, byte order is swapped
1441 for ($i = 0; $i < 28; $i += 2) {
1442 $ac3_data .= substr($testData, 8 + $i + 1, 1);
1443 $ac3_data .= substr($testData, 8 + $i + 0, 1);
1445 $getid3_ac3->AnalyzeString($ac3_data);
1448 if (empty($getid3_temp->info['error'])) {
1449 $info['audio'] = $getid3_temp->info['audio'];
1450 $info['ac3'] = $getid3_temp->info['ac3'];
1451 if (!empty($getid3_temp->info['warning'])) {
1452 foreach ($getid3_temp->info['warning'] as $newerror) {
1453 $this->warning('getid3_ac3() says: ['.$newerror.']');
1457 unset($getid3_temp, $getid3_ac3);
1459 } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1461 // This is probably DTS data
1462 $getid3_temp = new getID3();
1463 $getid3_temp->openfile($this->getid3->filename);
1464 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1465 $getid3_dts = new getid3_dts($getid3_temp);
1466 $getid3_dts->Analyze();
1467 if (empty($getid3_temp->info['error'])) {
1468 $info['audio'] = $getid3_temp->info['audio'];
1469 $info['dts'] = $getid3_temp->info['dts'];
1470 $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1471 if (!empty($getid3_temp->info['warning'])) {
1472 foreach ($getid3_temp->info['warning'] as $newerror) {
1473 $this->warning('getid3_dts() says: ['.$newerror.']');
1478 unset($getid3_temp, $getid3_dts);
1480 } elseif (substr($testData, 0, 4) == 'wvpk') {
1482 // This is WavPack data
1483 $info['wavpack']['offset'] = $info['avdataoffset'];
1484 $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1485 $this->parseWavPackHeader(substr($testData, 8, 28));
1488 // This is some other kind of data (quite possibly just PCM)
1489 // do nothing special, just skip it
1491 $nextoffset = $info['avdataend'];
1492 $this->fseek($nextoffset);
1504 // always read data in
1506 // should be: never read data in
1507 // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1508 if ($chunksize < 1048576) {
1509 if ($chunksize > 0) {
1510 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1511 if ($chunkname == 'JUNK') {
1512 if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1513 // only keep text characters [chr(32)-chr(127)]
1514 $info['riff']['comments']['junk'][] = trim($matches[1]);
1516 // but if nothing there, ignore
1517 // remove the key in either case
1518 unset($RIFFchunk[$chunkname][$thisindex]['data']);
1522 $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1523 $this->fseek($chunksize, SEEK_CUR);
1528 // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1532 if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1533 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1534 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1535 unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1536 unset($RIFFchunk[$chunkname][$thisindex]['size']);
1537 if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1538 unset($RIFFchunk[$chunkname][$thisindex]);
1540 if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1541 unset($RIFFchunk[$chunkname]);
1543 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1544 } elseif ($chunksize < 2048) {
1545 // only read data in if smaller than 2kB
1546 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1548 $this->fseek($chunksize, SEEK_CUR);
1556 } catch (getid3_exception $e) {
1557 if ($e->getCode() == 10) {
1558 $this->warning('RIFF parser: '.$e->getMessage());
1567 public function ParseRIFFdata(&$RIFFdata) {
1568 $info = &$this->getid3->info;
1570 $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1571 $fp_temp = fopen($tempfile, 'wb');
1572 $RIFFdataLength = strlen($RIFFdata);
1573 $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1574 for ($i = 0; $i < 4; $i++) {
1575 $RIFFdata[($i + 4)] = $NewLengthString[$i];
1577 fwrite($fp_temp, $RIFFdata);
1580 $getid3_temp = new getID3();
1581 $getid3_temp->openfile($tempfile);
1582 $getid3_temp->info['filesize'] = $RIFFdataLength;
1583 $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1584 $getid3_temp->info['tags'] = $info['tags'];
1585 $getid3_temp->info['warning'] = $info['warning'];
1586 $getid3_temp->info['error'] = $info['error'];
1587 $getid3_temp->info['comments'] = $info['comments'];
1588 $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1589 $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1590 $getid3_riff = new getid3_riff($getid3_temp);
1591 $getid3_riff->Analyze();
1593 $info['riff'] = $getid3_temp->info['riff'];
1594 $info['warning'] = $getid3_temp->info['warning'];
1595 $info['error'] = $getid3_temp->info['error'];
1596 $info['tags'] = $getid3_temp->info['tags'];
1597 $info['comments'] = $getid3_temp->info['comments'];
1598 unset($getid3_riff, $getid3_temp);
1604 public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1605 $RIFFinfoKeyLookup = array(
1606 'IARL'=>'archivallocation',
1608 'ICDS'=>'costumedesigner',
1609 'ICMS'=>'commissionedby',
1612 'ICOP'=>'copyright',
1613 'ICRD'=>'creationdate',
1614 'IDIM'=>'dimensions',
1615 'IDIT'=>'digitizationdate',
1616 'IDPI'=>'resolution',
1617 'IDST'=>'distributor',
1619 'IENG'=>'engineers',
1620 'IFRM'=>'accountofparts',
1623 'ILGT'=>'lightness',
1625 'IMED'=>'orignalmedium',
1628 'IPDS'=>'productiondesigner',
1636 'ISGN'=>'secondarygenre',
1637 'ISHP'=>'sharpness',
1638 'ISRC'=>'sourcesupplier',
1639 'ISRF'=>'digitizationsource',
1640 'ISTD'=>'productionstudio',
1642 'ITCH'=>'encoded_by',
1647 foreach ($RIFFinfoKeyLookup as $key => $value) {
1648 if (isset($RIFFinfoArray[$key])) {
1649 foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1650 if (trim($commentdata['data']) != '') {
1651 if (isset($CommentsTargetArray[$value])) {
1652 $CommentsTargetArray[$value][] = trim($commentdata['data']);
1654 $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1663 public static function parseWAVEFORMATex($WaveFormatExData) {
1665 $WaveFormatEx['raw'] = array();
1666 $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1668 $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2);
1669 $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2);
1670 $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4);
1671 $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4);
1672 $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2);
1673 $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2);
1674 if (strlen($WaveFormatExData) > 16) {
1675 $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2);
1677 $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1679 $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1680 $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1681 $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1682 $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1683 $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1685 return $WaveFormatEx;
1688 public function parseWavPackHeader($WavPackChunkData) {
1693 // short bits; // added for version 2.00
1694 // short flags, shift; // added for version 3.00
1695 // long total_samples, crc, crc2;
1696 // char extension [4], extra_bc, extras [3];
1700 $info = &$this->getid3->info;
1701 $info['wavpack'] = array();
1702 $thisfile_wavpack = &$info['wavpack'];
1704 $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1705 if ($thisfile_wavpack['version'] >= 2) {
1706 $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1708 if ($thisfile_wavpack['version'] >= 3) {
1709 $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1710 $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1711 $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1712 $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1713 $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1714 $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1715 $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1716 for ($i = 0; $i <= 2; $i++) {
1717 $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1721 $thisfile_wavpack['flags'] = array();
1722 $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1724 $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1725 $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1726 $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1727 $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1728 $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1729 $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1730 $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1731 $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1732 $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1733 $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1734 $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1735 $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1736 $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1737 $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1738 $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1739 $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1740 $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1741 $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1742 $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1743 $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1749 public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1751 $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1752 $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels
1753 $parsed['biHeight'] = 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
1754 $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1755 $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1756 $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1757 $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1758 $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1759 $parsed['biClrUsed'] = 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
1760 $parsed['biClrImportant'] = 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
1761 $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1763 $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1768 public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1769 // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1770 // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1771 // 'Byte Layout: '1111111111111111
1772 // '32 for Movie - 1 '1111111111111111
1773 // '28 for Author - 6 '6666666666666666
1774 // '4 for year - 2 '6666666666662222
1775 // '3 for genre - 3 '7777777777777777
1776 // '48 for Comments - 7 '7777777777777777
1777 // '1 for Rating - 4 '7777777777777777
1778 // '5 for Future Additions - 0 '333400000DIVXTAG
1781 static $DIVXTAGgenre = array(
1783 1 => 'Action/Adventure',
1795 13 => 'Infomercial',
1796 14 => 'Interactive',
1798 16 => 'Music Video',
1805 $DIVXTAGrating = array(
1814 $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1815 $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1816 $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1817 $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1818 $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1819 $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1820 //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1821 //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1823 $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1824 $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1827 unset($parsed['genre_id'], $parsed['rating_id']);
1828 foreach ($parsed as $key => $value) {
1829 if (!$value === '') {
1830 unset($parsed['key']);
1835 foreach ($parsed as $tag => $value) {
1836 $parsed[$tag] = array($value);
1842 public static function waveSNDMtagLookup($tagshortname) {
1845 /** This is not a comment!
1852 ©fin featuredinstrument
1862 return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
1865 public static function wFormatTagLookup($wFormatTag) {
1869 /** This is not a comment!
1871 0x0000 Microsoft Unknown Wave Format
1872 0x0001 Pulse Code Modulation (PCM)
1873 0x0002 Microsoft ADPCM
1875 0x0004 Compaq Computer VSELP
1877 0x0006 Microsoft A-Law
1878 0x0007 Microsoft mu-Law
1879 0x0008 Microsoft DTS
1881 0x0011 Intel DVI/IMA ADPCM
1882 0x0012 Videologic MediaSpace ADPCM
1883 0x0013 Sierra Semiconductor ADPCM
1884 0x0014 Antex Electronics G.723 ADPCM
1885 0x0015 DSP Solutions DigiSTD
1886 0x0016 DSP Solutions DigiFIX
1887 0x0017 Dialogic OKI ADPCM
1888 0x0018 MediaVision ADPCM
1889 0x0019 Hewlett-Packard CU
1891 0x0021 Speech Compression Sonarc
1892 0x0022 DSP Group TrueSpeech
1893 0x0023 Echo Speech EchoSC1
1894 0x0024 Audiofile AF36
1895 0x0025 Audio Processing Technology APTX
1896 0x0026 AudioFile AF10
1900 0x0031 Microsoft GSM 6.10
1902 0x0033 Antex Electronics ADPCME
1903 0x0034 Control Resources VQLPC
1904 0x0035 DSP Solutions DigiREAL
1905 0x0036 DSP Solutions DigiADPCM
1906 0x0037 Control Resources CR10
1907 0x0038 Natural MicroSystems VBXADPCM
1908 0x0039 Crystal Semiconductor IMA ADPCM
1910 0x003B Rockwell ADPCM
1911 0x003C Rockwell Digit LK
1913 0x0040 Antex Electronics G.721 ADPCM
1916 0x0050 MPEG Layer-2 or Layer-1
1924 0x0063 Canopus Atrac
1929 0x0069 Voxware Byte Aligned
1934 0x0074 Voxware MetaVoice
1935 0x0075 Voxware MetaSound
1936 0x0076 Voxware RT29HW
1950 0x0092 Dolby AC3 SPDIF
1951 0x0093 MediaSonic G.723
1952 0x0094 Aculab PLC Prosody 8kbps
1954 0x0098 Philips LPCBB
1957 0x0100 Rhetorex ADPCM
1960 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
1963 0x0123 Digital G.723
1964 0x0125 Sanyo LD ADPCM
1965 0x0130 Sipro Lab Telecom ACELP NET
1966 0x0131 Sipro Lab Telecom ACELP 4800
1967 0x0132 Sipro Lab Telecom ACELP 8V3
1968 0x0133 Sipro Lab Telecom G.729
1969 0x0134 Sipro Lab Telecom G.729A
1970 0x0135 Sipro Lab Telecom Kelvin
1971 0x0140 Windows Media Video V8
1972 0x0150 Qualcomm PureVoice
1973 0x0151 Qualcomm HalfRate
1974 0x0155 Ring Zero Systems TUB GSM
1975 0x0160 Microsoft Audio 1
1976 0x0161 Windows Media Audio V7 / V8 / V9
1977 0x0162 Windows Media Audio Professional V9
1978 0x0163 Windows Media Audio Lossless V9
1979 0x0200 Creative Labs ADPCM
1980 0x0202 Creative Labs Fastspeech8
1981 0x0203 Creative Labs Fastspeech10
1982 0x0210 UHER Informatic GmbH ADPCM
1984 0x0230 I-link Worldwide VC
1985 0x0240 Aureal RAW Sport
1986 0x0250 Interactive Products HSX
1987 0x0251 Interactive Products RPELP
1988 0x0260 Consistent Software CS2
1990 0x0300 Fujitsu FM Towns Snd
1992 0x0401 Intel Music Coder
1993 0x0450 QDesign Music
1995 0x0681 AT&T Labs TPC
1996 0x08AE ClearJump LiteWave
1998 0x1001 Olivetti ADPCM
1999 0x1002 Olivetti CELP
2002 0x1100 Lernout & Hauspie Codec (0x1100)
2003 0x1101 Lernout & Hauspie CELP Codec (0x1101)
2004 0x1102 Lernout & Hauspie SBC Codec (0x1102)
2005 0x1103 Lernout & Hauspie SBC Codec (0x1103)
2006 0x1104 Lernout & Hauspie SBC Codec (0x1104)
2008 0x1401 AT&T ISIAudio
2009 0x1500 Soundspace Music Compression
2010 0x181C VoxWare RT24 Speech
2011 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com)
2014 0x2002 WAVE_FORMAT_14_4
2015 0x2003 WAVE_FORMAT_28_8
2016 0x2004 WAVE_FORMAT_COOK
2017 0x2005 WAVE_FORMAT_DNET
2021 0x676F Ogg Vorbis 1+
2022 0x6770 Ogg Vorbis 2+
2023 0x6771 Ogg Vorbis 3+
2024 0x7A21 GSM-AMR (CBR, no SID)
2025 0x7A22 GSM-AMR (VBR, including SID)
2026 0xFFFE WAVE_FORMAT_EXTENSIBLE
2027 0xFFFF WAVE_FORMAT_DEVELOPMENT
2031 return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2034 public static function fourccLookup($fourcc) {
2038 /** This is not a comment!
2040 swot http://developer.apple.com/qa/snd/snd07.html
2041 ____ No Codec (____)
2042 _BIT BI_BITFIELDS (Raw RGB)
2043 _JPG JPEG compressed
2044 _PNG PNG compressed W3C/ISO/IEC (RFC-2083)
2045 _RAW Full Frames (Uncompressed)
2052 AASC Autodesk Animator
2053 ABYR Kensington ?ABYR?
2054 AEMI Array Microsystems VideoONE MPEG1-I Capture
2055 AFLC Autodesk Animator FLC
2056 AFLI Autodesk Animator FLI
2057 AMPG Array Microsystems VideoONE MPEG
2058 ANIM Intel RDX (ANIM)
2059 AP41 AngelPotion Definitive
2062 ASVX Asus Video 2.0 (audio)
2063 AUR2 AuraVision Aura 2 Codec - YUV 4:2:2
2064 AURA AuraVision Aura 1 Codec - YUV 4:1:1
2065 AVDJ Independent JPEG Group\'s codec (AVDJ)
2066 AVRN Independent JPEG Group\'s codec (AVRN)
2067 AYUV 4:4:4 YUV (AYUV)
2068 AZPR Quicktime Apple Video (AZPR)
2070 BLZ0 Blizzard DivX MPEG-4
2071 BTVC Conexant Composite Video
2072 BINK RAD Game Tools Bink Video
2073 BT20 Conexant Prosumer Video
2074 BTCV Conexant Composite Video Codec
2075 BW10 Data Translation Broadway MPEG Capture
2078 CFCC Digital Processing Systems DPS Perception
2079 CGDI Microsoft Office 97 Camcorder Video
2080 CHAM Winnov Caviara Champagne
2081 CJPG Creative WebCam JPEG
2082 CLJR Cirrus Logic YUV 4:1:1
2083 CMYK Common Data Format in Printing (Colorgraph)
2084 CPLA Weitek 4:2:0 YUV Planar
2085 CRAM Microsoft Video 1 (CRAM)
2088 CWLT Microsoft Color WLT DIB
2089 CYUV Creative Labs YUV
2093 DIB Device Independent Bitmap
2094 DIV1 FFmpeg OpenDivX
2095 DIV2 Microsoft MPEG-4 v1/v2
2096 DIV3 DivX ;-) MPEG-4 v3.x Low-Motion
2097 DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion
2098 DIV5 DivX MPEG-4 v5.x
2099 DIV6 DivX ;-) (MS MPEG-4 v3.x)
2100 DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo)
2102 DMB1 Matrox Rainbow Runner hardware MJPEG
2105 DUCK Duck TrueMotion 1.0
2106 DPS0 DPS/Leitch Reality Motion JPEG
2107 DPSC DPS/Leitch PAR Motion JPEG
2108 DV25 Matrox DVCPRO codec
2109 DV50 Matrox DVCPRO50 codec
2110 DVC IEC 61834 and SMPTE 314M (DVC/DV Video)
2111 DVCP IEC 61834 and SMPTE 314M (DVC/DV Video)
2112 DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps
2113 DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com)
2114 DVSL IEC Standard DV compressed in SD (SDL)
2116 DVE2 InSoft DVE-2 Videoconferencing
2117 dvsd IEC 61834 and SMPTE 314M DVC/DV Video
2118 DVSD IEC 61834 and SMPTE 314M DVC/DV Video
2119 DVX1 Lucent DVX1000SP Video Decoder
2120 DVX2 Lucent DVX2000S Video Decoder
2121 DVX3 Lucent DVX3000S Video Decoder
2123 DXT1 Microsoft DirectX Compressed Texture (DXT1)
2124 DXT2 Microsoft DirectX Compressed Texture (DXT2)