PageRenderTime 415ms CodeModel.GetById 161ms app.highlight 159ms RepoModel.GetById 52ms app.codeStats 2ms

/Vendor/getid3/module.audio-video.riff.php

https://bitbucket.org/nova-atlantis/simple-server-media-player
PHP | 2586 lines | 1541 code | 270 blank | 775 comment | 292 complexity | ee82540f026662197f7003474fd92de2 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1<?php
  2/////////////////////////////////////////////////////////////////
  3/// getID3() by James Heinrich <info@getid3.org>               //
  4//  available at http://getid3.sourceforge.net                 //
  5//            or http://www.getid3.org                         //
  6//          also https://github.com/JamesHeinrich/getID3       //
  7/////////////////////////////////////////////////////////////////
  8// See readme.txt for more details                             //
  9/////////////////////////////////////////////////////////////////
 10//                                                             //
 11// module.audio-video.riff.php                                 //
 12// module for analyzing RIFF files                             //
 13// multiple formats supported by this module:                  //
 14//    Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX   //
 15// dependencies: module.audio.mp3.php                          //
 16//               module.audio.ac3.php                          //
 17//               module.audio.dts.php                          //
 18//                                                            ///
 19/////////////////////////////////////////////////////////////////
 20
 21/**
 22* @todo Parse AC-3/DTS audio inside WAVE correctly
 23* @todo Rewrite RIFF parser totally
 24*/
 25
 26getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
 27getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
 28getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
 29
 30class getid3_riff extends getid3_handler {
 31
 32	protected $container = 'riff'; // default
 33
 34	public function Analyze() {
 35		$info = &$this->getid3->info;
 36
 37		// initialize these values to an empty array, otherwise they default to NULL
 38		// and you can't append array values to a NULL value
 39		$info['riff'] = array('raw'=>array());
 40
 41		// Shortcuts
 42		$thisfile_riff             = &$info['riff'];
 43		$thisfile_riff_raw         = &$thisfile_riff['raw'];
 44		$thisfile_audio            = &$info['audio'];
 45		$thisfile_video            = &$info['video'];
 46		$thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
 47		$thisfile_riff_audio       = &$thisfile_riff['audio'];
 48		$thisfile_riff_video       = &$thisfile_riff['video'];
 49
 50		$Original['avdataoffset'] = $info['avdataoffset'];
 51		$Original['avdataend']    = $info['avdataend'];
 52
 53		$this->fseek($info['avdataoffset']);
 54		$RIFFheader = $this->fread(12);
 55		$offset = $this->ftell();
 56		$RIFFtype    = substr($RIFFheader, 0, 4);
 57		$RIFFsize    = substr($RIFFheader, 4, 4);
 58		$RIFFsubtype = substr($RIFFheader, 8, 4);
 59
 60		switch ($RIFFtype) {
 61
 62			case 'FORM':  // AIFF, AIFC
 63				//$info['fileformat']   = 'aiff';
 64				$this->container = 'aiff';
 65				$thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
 66				$thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
 67				break;
 68
 69			case 'RIFF':  // AVI, WAV, etc
 70			case 'SDSS':  // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
 71			case 'RMP3':  // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
 72				//$info['fileformat']   = 'riff';
 73				$this->container = 'riff';
 74				$thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
 75				if ($RIFFsubtype == 'RMP3') {
 76					// RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
 77					$RIFFsubtype = 'WAVE';
 78				}
 79				if ($RIFFsubtype != 'AMV ') {
 80					// AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
 81					// Handled separately in ParseRIFFAMV()
 82					$thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
 83				}
 84				if (($info['avdataend'] - $info['filesize']) == 1) {
 85					// LiteWave appears to incorrectly *not* pad actual output file
 86					// to nearest WORD boundary so may appear to be short by one
 87					// byte, in which case - skip warning
 88					$info['avdataend'] = $info['filesize'];
 89				}
 90
 91				$nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
 92				while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
 93					try {
 94						$this->fseek($nextRIFFoffset);
 95					} catch (getid3_exception $e) {
 96						if ($e->getCode() == 10) {
 97							//$this->warning('RIFF parser: '.$e->getMessage());
 98							$this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
 99							$this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
100							break;
101						} else {
102							throw $e;
103						}
104					}
105					$nextRIFFheader = $this->fread(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
120					switch ($nextRIFFheaderID) {
121						case 'RIFF':
122							$chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
123							if (!isset($thisfile_riff[$nextRIFFtype])) {
124								$thisfile_riff[$nextRIFFtype] = array();
125							}
126							$thisfile_riff[$nextRIFFtype][] = $chunkdata;
127							break;
128
129						case 'AMV ':
130							unset($info['riff']);
131							$info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
132							break;
133
134						case 'JUNK':
135							// ignore
136							$thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
137							break;
138
139						case 'IDVX':
140							$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
141							break;
142
143						default:
144							if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
145								$DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
146								if (substr($DIVXTAG, -7) == 'DIVXTAG') {
147									// 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
148									$this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
149									$info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
150									break 2;
151								}
152							}
153							$this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
154							break 2;
155
156					}
157
158				}
159				if ($RIFFsubtype == 'WAVE') {
160					$thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
161				}
162				break;
163
164			default:
165				$this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
166				//unset($info['fileformat']);
167				return false;
168		}
169
170		$streamindex = 0;
171		switch ($RIFFsubtype) {
172
173			// http://en.wikipedia.org/wiki/Wav
174			case 'WAVE':
175				$info['fileformat'] = 'wav';
176
177				if (empty($thisfile_audio['bitrate_mode'])) {
178					$thisfile_audio['bitrate_mode'] = 'cbr';
179				}
180				if (empty($thisfile_audio_dataformat)) {
181					$thisfile_audio_dataformat = 'wav';
182				}
183
184				if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
185					$info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
186					$info['avdataend']    = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
187				}
188				if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
189
190					$thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
191					$thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
192					if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
193						$info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
194						return false;
195					}
196					$thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
197					unset($thisfile_riff_audio[$streamindex]['raw']);
198					$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
199
200					$thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
201					if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
202						$info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
203					}
204					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
205
206					if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
207						$info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
208					}
209
210					$thisfile_audio['lossless'] = false;
211					if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
212						switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
213
214							case 0x0001:  // PCM
215								$thisfile_audio['lossless'] = true;
216								break;
217
218							case 0x2000:  // AC-3
219								$thisfile_audio_dataformat = 'ac3';
220								break;
221
222							default:
223								// do nothing
224								break;
225
226						}
227					}
228					$thisfile_audio['streams'][$streamindex]['wformattag']   = $thisfile_audio['wformattag'];
229					$thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
230					$thisfile_audio['streams'][$streamindex]['lossless']     = $thisfile_audio['lossless'];
231					$thisfile_audio['streams'][$streamindex]['dataformat']   = $thisfile_audio_dataformat;
232				}
233
234				if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
235
236					// shortcuts
237					$rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
238					$thisfile_riff_raw['rgad']    = array('track'=>array(), 'album'=>array());
239					$thisfile_riff_raw_rgad       = &$thisfile_riff_raw['rgad'];
240					$thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
241					$thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
242
243					$thisfile_riff_raw_rgad['fPeakAmplitude']      = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
244					$thisfile_riff_raw_rgad['nRadioRgAdjust']      =        $this->EitherEndian2Int(substr($rgadData, 4, 2));
245					$thisfile_riff_raw_rgad['nAudiophileRgAdjust'] =        $this->EitherEndian2Int(substr($rgadData, 6, 2));
246
247					$nRadioRgAdjustBitstring      = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
248					$nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
249					$thisfile_riff_raw_rgad_track['name']       = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
250					$thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
251					$thisfile_riff_raw_rgad_track['signbit']    = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
252					$thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
253					$thisfile_riff_raw_rgad_album['name']       = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
254					$thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
255					$thisfile_riff_raw_rgad_album['signbit']    = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
256					$thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
257
258					$thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
259					if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
260						$thisfile_riff['rgad']['track']['name']            = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
261						$thisfile_riff['rgad']['track']['originator']      = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
262						$thisfile_riff['rgad']['track']['adjustment']      = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
263					}
264					if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
265						$thisfile_riff['rgad']['album']['name']       = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
266						$thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
267						$thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
268					}
269				}
270
271				if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
272					$thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
273
274					// This should be a good way of calculating exact playtime,
275					// but some sample files have had incorrect number of samples,
276					// so cannot use this method
277
278					// if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
279					//     $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
280					// }
281				}
282				if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
283					$thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
284				}
285
286				if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
287					// shortcut
288					$thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
289
290					$thisfile_riff_WAVE_bext_0['title']          =                         trim(substr($thisfile_riff_WAVE_bext_0['data'],   0, 256));
291					$thisfile_riff_WAVE_bext_0['author']         =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 256,  32));
292					$thisfile_riff_WAVE_bext_0['reference']      =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 288,  32));
293					$thisfile_riff_WAVE_bext_0['origin_date']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 320,  10);
294					$thisfile_riff_WAVE_bext_0['origin_time']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 330,   8);
295					$thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338,   8));
296					$thisfile_riff_WAVE_bext_0['bwf_version']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346,   1));
297					$thisfile_riff_WAVE_bext_0['reserved']       =                              substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
298					$thisfile_riff_WAVE_bext_0['coding_history'] =         explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
299					if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
300						if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
301							list($dummy, $bext_timestamp['year'], $bext_timestamp['month'],  $bext_timestamp['day'])    = $matches_bext_date;
302							list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
303							$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']);
304						} else {
305							$info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
306						}
307					} else {
308						$info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
309					}
310					$thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
311					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
312				}
313
314				if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
315					// shortcut
316					$thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
317
318					$thisfile_riff_WAVE_MEXT_0['raw']['sound_information']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
319					$thisfile_riff_WAVE_MEXT_0['flags']['homogenous']           = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
320					if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
321						$thisfile_riff_WAVE_MEXT_0['flags']['padding']          = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
322						$thisfile_riff_WAVE_MEXT_0['flags']['22_or_44']         =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
323						$thisfile_riff_WAVE_MEXT_0['flags']['free_format']      =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
324
325						$thisfile_riff_WAVE_MEXT_0['nominal_frame_size']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
326					}
327					$thisfile_riff_WAVE_MEXT_0['anciliary_data_length']         = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
328					$thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def']     = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
329					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
330					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
331					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
332				}
333
334				if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
335					// shortcut
336					$thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
337
338					$thisfile_riff_WAVE_cart_0['version']              =                              substr($thisfile_riff_WAVE_cart_0['data'],   0,  4);
339					$thisfile_riff_WAVE_cart_0['title']                =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],   4, 64));
340					$thisfile_riff_WAVE_cart_0['artist']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],  68, 64));
341					$thisfile_riff_WAVE_cart_0['cut_id']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
342					$thisfile_riff_WAVE_cart_0['client_id']            =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
343					$thisfile_riff_WAVE_cart_0['category']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
344					$thisfile_riff_WAVE_cart_0['classification']       =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
345					$thisfile_riff_WAVE_cart_0['out_cue']              =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
346					$thisfile_riff_WAVE_cart_0['start_date']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
347					$thisfile_riff_WAVE_cart_0['start_time']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 462,  8));
348					$thisfile_riff_WAVE_cart_0['end_date']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
349					$thisfile_riff_WAVE_cart_0['end_time']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 480,  8));
350					$thisfile_riff_WAVE_cart_0['producer_app_id']      =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
351					$thisfile_riff_WAVE_cart_0['producer_app_version'] =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
352					$thisfile_riff_WAVE_cart_0['user_defined_text']    =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
353					$thisfile_riff_WAVE_cart_0['zero_db_reference']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680,  4), true);
354					for ($i = 0; $i < 8; $i++) {
355						$thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] =                  substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
356						$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));
357					}
358					$thisfile_riff_WAVE_cart_0['url']              =                 trim(substr($thisfile_riff_WAVE_cart_0['data'],  748, 1024));
359					$thisfile_riff_WAVE_cart_0['tag_text']         = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
360
361					$thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
362					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_cart_0['title'];
363				}
364
365				if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
366					// SoundMiner metadata
367
368					// shortcuts
369					$thisfile_riff_WAVE_SNDM_0      = &$thisfile_riff_WAVE['SNDM'][0];
370					$thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
371					$SNDM_startoffset = 0;
372					$SNDM_endoffset   = $thisfile_riff_WAVE_SNDM_0['size'];
373
374					while ($SNDM_startoffset < $SNDM_endoffset) {
375						$SNDM_thisTagOffset = 0;
376						$SNDM_thisTagSize      = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
377						$SNDM_thisTagOffset += 4;
378						$SNDM_thisTagKey       =                           substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
379						$SNDM_thisTagOffset += 4;
380						$SNDM_thisTagDataSize  = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
381						$SNDM_thisTagOffset += 2;
382						$SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
383						$SNDM_thisTagOffset += 2;
384						$SNDM_thisTagDataText =                            substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
385						$SNDM_thisTagOffset += $SNDM_thisTagDataSize;
386
387						if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
388							$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).')';
389							break;
390						} elseif ($SNDM_thisTagSize <= 0) {
391							$info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
392							break;
393						}
394						$SNDM_startoffset += $SNDM_thisTagSize;
395
396						$thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
397						if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
398							$thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
399						} else {
400							$info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
401						}
402					}
403
404					$tagmapping = array(
405						'tracktitle'=>'title',
406						'category'  =>'genre',
407						'cdtitle'   =>'album',
408						'tracktitle'=>'title',
409					);
410					foreach ($tagmapping as $fromkey => $tokey) {
411						if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
412							$thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
413						}
414					}
415				}
416
417				if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
418					// requires functions simplexml_load_string and get_object_vars
419					if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
420						$thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
421						if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
422							@list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
423							$thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
424						}
425						if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
426							@list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
427							$thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
428						}
429						if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
430							$samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
431							$thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
432							$h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
433							$m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600))      / 60);
434							$s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
435							$f =       ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
436							$thisfile_riff_WAVE['iXML'][0]['timecode_string']       = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s,       $f);
437							$thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d',   $h, $m, $s, round($f));
438						}
439						unset($parsedXML);
440					}
441				}
442
443
444
445				if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
446					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
447					$info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
448				}
449
450				if (!empty($info['wavpack'])) {
451					$thisfile_audio_dataformat = 'wavpack';
452					$thisfile_audio['bitrate_mode'] = 'vbr';
453					$thisfile_audio['encoder']      = 'WavPack v'.$info['wavpack']['version'];
454
455					// Reset to the way it was - RIFF parsing will have messed this up
456					$info['avdataend']        = $Original['avdataend'];
457					$thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
458
459					$this->fseek($info['avdataoffset'] - 44);
460					$RIFFdata = $this->fread(44);
461					$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
462					$OrignalRIFFdataSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
463
464					if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
465						$info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
466						$this->fseek($info['avdataend']);
467						$RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
468					}
469
470					// move the data chunk after all other chunks (if any)
471					// so that the RIFF parser doesn't see EOF when trying
472					// to skip over the data chunk
473					$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
474					$getid3_riff = new getid3_riff($this->getid3);
475					$getid3_riff->ParseRIFFdata($RIFFdata);
476					unset($getid3_riff);
477				}
478
479				if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
480					switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
481						case 0x0001: // PCM
482							if (!empty($info['ac3'])) {
483								// Dolby Digital WAV files masquerade as PCM-WAV, but they're not
484								$thisfile_audio['wformattag']  = 0x2000;
485								$thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
486								$thisfile_audio['lossless']    = false;
487								$thisfile_audio['bitrate']     = $info['ac3']['bitrate'];
488								$thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
489							}
490							if (!empty($info['dts'])) {
491								// Dolby DTS files masquerade as PCM-WAV, but they're not
492								$thisfile_audio['wformattag']  = 0x2001;
493								$thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
494								$thisfile_audio['lossless']    = false;
495								$thisfile_audio['bitrate']     = $info['dts']['bitrate'];
496								$thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
497							}
498							break;
499						case 0x08AE: // ClearJump LiteWave
500							$thisfile_audio['bitrate_mode'] = 'vbr';
501							$thisfile_audio_dataformat   = 'litewave';
502
503							//typedef struct tagSLwFormat {
504							//  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
505							//  DWORD   m_dwScale;         // scale factor for lossy compression
506							//  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
507							//  WORD    m_wQuality;        // alias for the scale factor
508							//  WORD    m_wMarkDistance;   // distance between marks in bytes
509							//  WORD    m_wReserved;
510							//
511							//  //following paramters are ignored if CF_FILESRC is not set
512							//  DWORD   m_dwOrgSize;       // original file size in bytes
513							//  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
514							//  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
515							//
516							//  PCMWAVEFORMAT m_OrgWf;     // original wave format
517							// }SLwFormat, *PSLwFormat;
518
519							// shortcut
520							$thisfile_riff['litewave']['raw'] = array();
521							$riff_litewave     = &$thisfile_riff['litewave'];
522							$riff_litewave_raw = &$riff_litewave['raw'];
523
524							$flags = array(
525								'compression_method' => 1,
526								'compression_flags'  => 1,
527								'm_dwScale'          => 4,
528								'm_dwBlockSize'      => 4,
529								'm_wQuality'         => 2,
530								'm_wMarkDistance'    => 2,
531								'm_wReserved'        => 2,
532								'm_dwOrgSize'        => 4,
533								'm_bFactExists'      => 2,
534								'm_dwRiffChunkSize'  => 4,
535							);
536							$litewave_offset = 18;
537							foreach ($flags as $flag => $length) {
538								$riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
539								$litewave_offset += $length;
540							}
541
542							//$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
543							$riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
544
545							$riff_litewave['flags']['raw_source']    = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
546							$riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
547							$riff_litewave['flags']['seekpoints']    =        (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
548
549							$thisfile_audio['lossless']        = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
550							$thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
551							break;
552
553						default:
554							break;
555					}
556				}
557				if ($info['avdataend'] > $info['filesize']) {
558					switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
559						case 'wavpack': // WavPack
560						case 'lpac':    // LPAC
561						case 'ofr':     // OptimFROG
562						case 'ofs':     // OptimFROG DualStream
563							// lossless compressed audio formats that keep original RIFF headers - skip warning
564							break;
565
566						case 'litewave':
567							if (($info['avdataend'] - $info['filesize']) == 1) {
568								// LiteWave appears to incorrectly *not* pad actual output file
569								// to nearest WORD boundary so may appear to be short by one
570								// byte, in which case - skip warning
571							} else {
572								// Short by more than one byte, throw warning
573								$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)';
574								$info['avdataend'] = $info['filesize'];
575							}
576							break;
577
578						default:
579							if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
580								// output file appears to be incorrectly *not* padded to nearest WORD boundary
581								// Output less severe warning
582								$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)';
583								$info['avdataend'] = $info['filesize'];
584							} else {
585								// Short by more than one byte, throw warning
586								$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)';
587								$info['avdataend'] = $info['filesize'];
588							}
589							break;
590					}
591				}
592				if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
593					if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
594						$info['avdataend']--;
595						$info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
596					}
597				}
598				if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
599					unset($thisfile_audio['bits_per_sample']);
600					if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
601						$thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
602					}
603				}
604				break;
605
606			// http://en.wikipedia.org/wiki/Audio_Video_Interleave
607			case 'AVI ':
608				$info['fileformat'] = 'avi';
609				$info['mime_type']  = 'video/avi';
610
611				$thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
612				$thisfile_video['dataformat']   = 'avi';
613
614				if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
615					$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
616					if (isset($thisfile_riff['AVIX'])) {
617						$info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
618					} else {
619						$info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
620					}
621					if ($info['avdataend'] > $info['filesize']) {
622						$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)';
623						$info['avdataend'] = $info['filesize'];
624					}
625				}
626
627				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
628					//$bIndexType = array(
629					//	0x00 => 'AVI_INDEX_OF_INDEXES',
630					//	0x01 => 'AVI_INDEX_OF_CHUNKS',
631					//	0x80 => 'AVI_INDEX_IS_DATA',
632					//);
633					//$bIndexSubtype = array(
634					//	0x01 => array(
635					//		0x01 => 'AVI_INDEX_2FIELD',
636					//	),
637					//);
638					foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
639						$ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
640
641						$thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd,  0, 2));
642						$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']  = $this->EitherEndian2Int(substr($ahsisd,  2, 1));
643						$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']     = $this->EitherEndian2Int(substr($ahsisd,  3, 1));
644						$thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse']  = $this->EitherEndian2Int(substr($ahsisd,  4, 4));
645						$thisfile_riff_raw['indx'][$streamnumber]['dwChunkId']      =                         substr($ahsisd,  8, 4);
646						$thisfile_riff_raw['indx'][$streamnumber]['dwReserved']     = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
647
648						//$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
649						//$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
650
651						unset($ahsisd);
652					}
653				}
654				if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
655					$avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
656
657					// shortcut
658					$thisfile_riff_raw['avih'] = array();
659					$thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
660
661					$thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
662					if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
663						$info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
664						return false;
665					}
666
667					$flags = array(
668						'dwMaxBytesPerSec',       // max. transfer rate
669						'dwPaddingGranularity',   // pad to multiples of this size; normally 2K.
670						'dwFlags',                // the ever-present flags
671						'dwTotalFrames',          // # frames in file
672						'dwInitialFrames',        //
673						'dwStreams',              //
674						'dwSuggestedBufferSize',  //
675						'dwWidth',                //
676						'dwHeight',               //
677						'dwScale',                //
678						'dwRate',                 //
679						'dwStart',                //
680						'dwLength',               //
681					);
682					$avih_offset = 4;
683					foreach ($flags as $flag) {
684						$thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
685						$avih_offset += 4;
686					}
687
688					$flags = array(
689						'hasindex'     => 0x00000010,
690						'mustuseindex' => 0x00000020,
691						'interleaved'  => 0x00000100,
692						'trustcktype'  => 0x00000800,
693						'capturedfile' => 0x00010000,
694						'copyrighted'  => 0x00020010,
695					);
696                    foreach ($flags as $flag => $value) {
697						$thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
698					}
699
700					// shortcut
701					$thisfile_riff_video[$streamindex] = array();
702					$thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
703
704					if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
705						$thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
706						$thisfile_video['resolution_x']             = $thisfile_riff_video_current['frame_width'];
707					}
708					if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
709						$thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
710						$thisfile_video['resolution_y']              = $thisfile_riff_video_current['frame_height'];
711					}
712					if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
713						$thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
714						$thisfile_video['total_frames']              = $thisfile_riff_video_current['total_frames'];
715					}
716
717					$thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
718					$thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
719				}
720				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
721					if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
722						for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
723							if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
724								$strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
725								$strhfccType = substr($strhData,  0, 4);
726
727								if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
728									$strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
729
730									// shortcut
731									$thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
732
733									switch ($strhfccType) {
734										case 'auds':
735											$thisfile_audio['bitrate_mode'] = 'cbr';
736											$thisfile_audio_dataformat      = 'wav';
737											if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
738												$streamindex = count($thisfile_riff_audio);
739											}
740
741											$thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
742											$thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
743
744											// shortcut
745											$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
746											$thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
747
748											if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
749												unset($thisfile_audio_streams_currentstream['bits_per_sample']);
750											}
751											$thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
752											unset($thisfile_audio_streams_currentstream['raw']);
753
754											// shortcut
755											$thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
756
757											unset($thisfile_riff_audio[$streamindex]['raw']);
758											$thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
759
760											$thisfile_audio['lossless'] = false;
761											switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
762												case 0x0001:  // PCM
763													$thisfile_audio_dataformat  = 'wav';
764													$thisfile_audio['lossless'] = true;
765													break;
766
767												case 0x0050: // MPEG Layer 2 or Layer 1
768													$thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
769													break;
770
771												case 0x0055: // MPEG Layer 3
772													$thisfile_audio_dataformat = 'mp3';
773													break;
774
775												case 0x00FF: // AAC
776													$thisfile_audio_dataformat = 'aac';
777													break;
778
779												case 0x0161: // Windows Media v7 / v8 / v9
780												case 0x0162: // Windows Media Professional v9
781												case 0x0163: // Windows Media Lossess v9
782													$thisfile_audio_dataformat = 'wma';
783													break;
784
785												case 0x2000: // AC-3
786													$thisfile_audio_dataformat = 'ac3';
787													break;
788
789												case 0x2001: // DTS
790													$thisfile_audio_dataformat = 'dts';
791													break;
792
793												default:
794													$thisfile_audio_dataformat = 'wav';
795													break;
796											}
797											$thisfile_audio_streams_currentstream['dataformat']   = $thisfile_audio_dataformat;
798											$thisfile_audio_streams_currentstream['lossless']     = $thisfile_audio['lossless'];
799											$thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
800											break;
801
802
803										case 'iavs':
804										case 'vids':
805											// shortcut
806											$thisfile_riff_raw['strh'][$i]                  = array();
807											$thisfile_riff_raw_strh_current                 = &$thisfile_riff_raw['strh'][$i];
808
809											$thisfile_riff_raw_strh_current['fccType']               =                         substr($strhData,  0, 4);  // same as $strhfccType;
810											$thisfile_riff_raw_strh_current['fccHandler']            =                         substr($strhData,  4, 4);
811											$thisfile_riff_raw_strh_current['dwFlags']               = $this->EitherEndian2Int(substr($strhData,  8, 4)); // Contains AVITF_* flags
812											$thisfile_riff_raw_strh_current['wPriority']             = $this->EitherEndian2Int(substr($strhData, 12, 2));
813											$thisfile_riff_raw_strh_current['wLanguage']             = $this->EitherEndian2Int(substr($strhData, 14, 2));
814											$thisfile_riff_raw_strh_current['dwInitialFrames']       = $this->EitherEndian2Int(substr($strhData, 16, 4));
815											$thisfile_riff_raw_strh_current['dwScale']               = $this->EitherEndian2Int(substr($strhData, 20, 4));
816											$thisfile_riff_raw_strh_current['dwRate']                = $this->EitherEndian2Int(substr($strhData, 24, 4));
817											$thisfile_riff_raw_strh_current['dwStart']               = $this->EitherEndian2Int(substr($strhData, 28, 4));
818											$thisfile_riff_raw_strh_current['dwLength']              = $this->EitherEndian2Int(substr($strhData, 32, 4));
819											$thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
820											$thisfile_riff_raw_strh_current['dwQuality']             = $this->EitherEndian2Int(substr($strhData, 40, 4));
821											$thisfile_riff_raw_strh_current['dwSampleSize']          = $this->EitherEndian2Int(substr($strhData, 44, 4));
822											$thisfile_riff_raw_strh_current['rcFrame']               = $this->EitherEndian2Int(substr($strhData, 48, 4));
823
824											$thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
825											$thisfile_video['fourcc']             = $thisfile_riff_raw_strh_current['fccHandler'];
826											if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
827												$thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
828												$thisfile_video['fourcc']             = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
829											}
830											$thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
831											$thisfile_video['pixel_aspect_ratio'] = (float) 1;
832											switch ($thisfile_riff_raw_strh_current['fccHandler']) {
833												case 'HFYU': // Huffman Lossless Codec
834												case 'IRAW': // Intel YUV Uncompressed
835												case 'YUY2': // Uncompressed YUV 4:2:2
836													$thisfile_video['lossless'] = true;
837													break;
838
839												default:
840													$thisfile_video['lossless'] = false;
841													break;
842											}
843
844											switch ($strhfccType) {
845												case 'vids':
846													$thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
847													$thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
848
849													if ($thisfile_riff_video_current['codec'] == 'DV') {
850														$thisfile_riff_video_current['dv_type'] = 2;
851													}
852													break;
853
854												case 'iavs':
855													$thisfile_riff_video_current['dv_type'] = 1;
856													break;
857											}
858											break;
859
860										default:
861											$info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
862											break;
863
864									}
865								}
866							}
867
868							if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
869
870								$thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
871								if (self::fourccLookup($thisfile_video['fourcc'])) {
872									$thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
873									$thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
874								}
875
876								switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
877									case 'HFYU': // Huffman Lossless Codec
878									case 'IRAW': // Intel YUV Uncompressed
879									case 'YUY2': // Uncompressed YUV 4:2:2
880										$thisfile_video['lossless']        = true;
881										//$thisfile_video['bits_per_sample'] = 24;
882										break;
883
884									default:
885										$thisfile_video['lossless']        = false;
886										//$thisfile_video['bits_per_sample'] = 24;
887										break;
888								}
889
890							}
891						}
892					}
893				}
894				break;
895
896
897			case 'AMV ':
898				$info['fileformat'] = 'amv';
899				$info['mime_type']  = 'video/amv';
900
901				$thisfile_video['bitrate_mode']    = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
902				$thisfile_video['dataformat']      = 'mjpeg';
903				$thisfile_video['codec']           = 'mjpeg';
904				$thisfile_video['lossless']        = false;
905				$thisfile_video['bits_per_sample'] = 24;
906
907				$thisfile_audio['dataformat']   = 'adpcm';
908				$thisfile_audio['lossless']     = false;
909				break;
910
911
912			// http://en.wikipedia.org/wiki/CD-DA
913			case 'CDDA':
914				$info['fileformat'] = 'cda';
915			    unset($info['mime_type']);
916
917				$thisfile_audio_dataformat      = 'cda';
918
919				$info['avdataoffset'] = 44;
920
921				if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
922					// shortcut
923					$thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
924
925					$thisfile_riff_CDDA_fmt_0['unknown1']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  0, 2));
926					$thisfile_riff_CDDA_fmt_0['track_num']          = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  2, 2));
927					$thisfile_riff_CDDA_fmt_0['disc_id']            = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  4, 4));
928			

Large files files are truncated, but you can click here to view the full file