ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
module.audio-video.riff.php
Go to the documentation of this file.
1 <?php
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
8 // See readme.txt for more details //
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 // ///
20 
26 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29 
30 class 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  $this->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  $this->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  $this->warning('RIFF.WAVE.BEXT.origin_time is invalid');
306  }
307  } else {
308  $this->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  $this->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  $this->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  $this->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  $timestamp_sample_rate = (is_array($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) ? max($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) : $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']); // XML could possibly contain more than one TIMESTAMP_SAMPLE_RATE tag, returning as array instead of integer [why? does it make sense? perhaps doesn't matter but getID3 needs to deal with it] - see https://github.com/JamesHeinrich/getID3/issues/105
432  $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $timestamp_sample_rate;
433  $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
434  $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
435  $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
436  $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
437  $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
438  $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
439  unset($samples_since_midnight, $timestamp_sample_rate, $h, $m, $s, $f);
440  }
441  unset($parsedXML);
442  }
443  }
444 
445 
446 
447  if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
448  $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
449  $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
450  }
451 
452  if (!empty($info['wavpack'])) {
453  $thisfile_audio_dataformat = 'wavpack';
454  $thisfile_audio['bitrate_mode'] = 'vbr';
455  $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
456 
457  // Reset to the way it was - RIFF parsing will have messed this up
458  $info['avdataend'] = $Original['avdataend'];
459  $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
460 
461  $this->fseek($info['avdataoffset'] - 44);
462  $RIFFdata = $this->fread(44);
463  $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
464  $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
465 
466  if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
467  $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
468  $this->fseek($info['avdataend']);
469  $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
470  }
471 
472  // move the data chunk after all other chunks (if any)
473  // so that the RIFF parser doesn't see EOF when trying
474  // to skip over the data chunk
475  $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
476  $getid3_riff = new getid3_riff($this->getid3);
477  $getid3_riff->ParseRIFFdata($RIFFdata);
478  unset($getid3_riff);
479  }
480 
481  if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
482  switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
483  case 0x0001: // PCM
484  if (!empty($info['ac3'])) {
485  // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
486  $thisfile_audio['wformattag'] = 0x2000;
487  $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
488  $thisfile_audio['lossless'] = false;
489  $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
490  $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
491  }
492  if (!empty($info['dts'])) {
493  // Dolby DTS files masquerade as PCM-WAV, but they're not
494  $thisfile_audio['wformattag'] = 0x2001;
495  $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
496  $thisfile_audio['lossless'] = false;
497  $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
498  $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
499  }
500  break;
501  case 0x08AE: // ClearJump LiteWave
502  $thisfile_audio['bitrate_mode'] = 'vbr';
503  $thisfile_audio_dataformat = 'litewave';
504 
505  //typedef struct tagSLwFormat {
506  // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags
507  // DWORD m_dwScale; // scale factor for lossy compression
508  // DWORD m_dwBlockSize; // number of samples in encoded blocks
509  // WORD m_wQuality; // alias for the scale factor
510  // WORD m_wMarkDistance; // distance between marks in bytes
511  // WORD m_wReserved;
512  //
513  // //following paramters are ignored if CF_FILESRC is not set
514  // DWORD m_dwOrgSize; // original file size in bytes
515  // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file
516  // DWORD m_dwRiffChunkSize; // riff chunk size in the original file
517  //
518  // PCMWAVEFORMAT m_OrgWf; // original wave format
519  // }SLwFormat, *PSLwFormat;
520 
521  // shortcut
522  $thisfile_riff['litewave']['raw'] = array();
523  $riff_litewave = &$thisfile_riff['litewave'];
524  $riff_litewave_raw = &$riff_litewave['raw'];
525 
526  $flags = array(
527  'compression_method' => 1,
528  'compression_flags' => 1,
529  'm_dwScale' => 4,
530  'm_dwBlockSize' => 4,
531  'm_wQuality' => 2,
532  'm_wMarkDistance' => 2,
533  'm_wReserved' => 2,
534  'm_dwOrgSize' => 4,
535  'm_bFactExists' => 2,
536  'm_dwRiffChunkSize' => 4,
537  );
538  $litewave_offset = 18;
539  foreach ($flags as $flag => $length) {
540  $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
541  $litewave_offset += $length;
542  }
543 
544  //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
545  $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
546 
547  $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
548  $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
549  $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
550 
551  $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
552  $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
553  break;
554 
555  default:
556  break;
557  }
558  }
559  if ($info['avdataend'] > $info['filesize']) {
560  switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
561  case 'wavpack': // WavPack
562  case 'lpac': // LPAC
563  case 'ofr': // OptimFROG
564  case 'ofs': // OptimFROG DualStream
565  // lossless compressed audio formats that keep original RIFF headers - skip warning
566  break;
567 
568  case 'litewave':
569  if (($info['avdataend'] - $info['filesize']) == 1) {
570  // LiteWave appears to incorrectly *not* pad actual output file
571  // to nearest WORD boundary so may appear to be short by one
572  // byte, in which case - skip warning
573  } else {
574  // Short by more than one byte, throw warning
575  $this->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)');
576  $info['avdataend'] = $info['filesize'];
577  }
578  break;
579 
580  default:
581  if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
582  // output file appears to be incorrectly *not* padded to nearest WORD boundary
583  // Output less severe warning
584  $this->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)');
585  $info['avdataend'] = $info['filesize'];
586  } else {
587  // Short by more than one byte, throw warning
588  $this->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)');
589  $info['avdataend'] = $info['filesize'];
590  }
591  break;
592  }
593  }
594  if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
595  if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
596  $info['avdataend']--;
597  $this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored');
598  }
599  }
600  if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
601  unset($thisfile_audio['bits_per_sample']);
602  if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
603  $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
604  }
605  }
606  break;
607 
608  // http://en.wikipedia.org/wiki/Audio_Video_Interleave
609  case 'AVI ':
610  $info['fileformat'] = 'avi';
611  $info['mime_type'] = 'video/avi';
612 
613  $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
614  $thisfile_video['dataformat'] = 'avi';
615 
616  if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
617  $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
618  if (isset($thisfile_riff['AVIX'])) {
619  $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
620  } else {
621  $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
622  }
623  if ($info['avdataend'] > $info['filesize']) {
624  $this->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)');
625  $info['avdataend'] = $info['filesize'];
626  }
627  }
628 
629  if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
630  //$bIndexType = array(
631  // 0x00 => 'AVI_INDEX_OF_INDEXES',
632  // 0x01 => 'AVI_INDEX_OF_CHUNKS',
633  // 0x80 => 'AVI_INDEX_IS_DATA',
634  //);
635  //$bIndexSubtype = array(
636  // 0x01 => array(
637  // 0x01 => 'AVI_INDEX_2FIELD',
638  // ),
639  //);
640  foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
641  $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
642 
643  $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
644  $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
645  $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
646  $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
647  $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
648  $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
649 
650  //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
651  //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
652 
653  unset($ahsisd);
654  }
655  }
656  if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
657  $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
658 
659  // shortcut
660  $thisfile_riff_raw['avih'] = array();
661  $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
662 
663  $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
664  if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
665  $this->error('Corrupt RIFF file: avih.dwMicroSecPerFrame == zero');
666  return false;
667  }
668 
669  $flags = array(
670  'dwMaxBytesPerSec', // max. transfer rate
671  'dwPaddingGranularity', // pad to multiples of this size; normally 2K.
672  'dwFlags', // the ever-present flags
673  'dwTotalFrames', // # frames in file
674  'dwInitialFrames', //
675  'dwStreams', //
676  'dwSuggestedBufferSize', //
677  'dwWidth', //
678  'dwHeight', //
679  'dwScale', //
680  'dwRate', //
681  'dwStart', //
682  'dwLength', //
683  );
684  $avih_offset = 4;
685  foreach ($flags as $flag) {
686  $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
687  $avih_offset += 4;
688  }
689 
690  $flags = array(
691  'hasindex' => 0x00000010,
692  'mustuseindex' => 0x00000020,
693  'interleaved' => 0x00000100,
694  'trustcktype' => 0x00000800,
695  'capturedfile' => 0x00010000,
696  'copyrighted' => 0x00020010,
697  );
698  foreach ($flags as $flag => $value) {
699  $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
700  }
701 
702  // shortcut
703  $thisfile_riff_video[$streamindex] = array();
704  $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
705 
706  if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
707  $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
708  $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
709  }
710  if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
711  $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
712  $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
713  }
714  if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
715  $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
716  $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
717  }
718 
719  $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
720  $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
721  }
722  if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
723  if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
724  for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
725  if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
726  $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
727  $strhfccType = substr($strhData, 0, 4);
728 
729  if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
730  $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
731 
732  // shortcut
733  $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
734 
735  switch ($strhfccType) {
736  case 'auds':
737  $thisfile_audio['bitrate_mode'] = 'cbr';
738  $thisfile_audio_dataformat = 'wav';
739  if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
740  $streamindex = count($thisfile_riff_audio);
741  }
742 
743  $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
744  $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
745 
746  // shortcut
747  $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
748  $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
749 
750  if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
751  unset($thisfile_audio_streams_currentstream['bits_per_sample']);
752  }
753  $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
754  unset($thisfile_audio_streams_currentstream['raw']);
755 
756  // shortcut
757  $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
758 
759  unset($thisfile_riff_audio[$streamindex]['raw']);
760  $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
761 
762  $thisfile_audio['lossless'] = false;
763  switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
764  case 0x0001: // PCM
765  $thisfile_audio_dataformat = 'wav';
766  $thisfile_audio['lossless'] = true;
767  break;
768 
769  case 0x0050: // MPEG Layer 2 or Layer 1
770  $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
771  break;
772 
773  case 0x0055: // MPEG Layer 3
774  $thisfile_audio_dataformat = 'mp3';
775  break;
776 
777  case 0x00FF: // AAC
778  $thisfile_audio_dataformat = 'aac';
779  break;
780 
781  case 0x0161: // Windows Media v7 / v8 / v9
782  case 0x0162: // Windows Media Professional v9
783  case 0x0163: // Windows Media Lossess v9
784  $thisfile_audio_dataformat = 'wma';
785  break;
786 
787  case 0x2000: // AC-3
788  $thisfile_audio_dataformat = 'ac3';
789  break;
790 
791  case 0x2001: // DTS
792  $thisfile_audio_dataformat = 'dts';
793  break;
794 
795  default:
796  $thisfile_audio_dataformat = 'wav';
797  break;
798  }
799  $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
800  $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
801  $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
802  break;
803 
804 
805  case 'iavs':
806  case 'vids':
807  // shortcut
808  $thisfile_riff_raw['strh'][$i] = array();
809  $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
810 
811  $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
812  $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
813  $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
814  $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
815  $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
816  $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
817  $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
818  $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
819  $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
820  $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
821  $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
822  $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
823  $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
824  $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
825 
826  $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
827  $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
828  if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
829  $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
830  $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
831  }
832  $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
833  $thisfile_video['pixel_aspect_ratio'] = (float) 1;
834  switch ($thisfile_riff_raw_strh_current['fccHandler']) {
835  case 'HFYU': // Huffman Lossless Codec
836  case 'IRAW': // Intel YUV Uncompressed
837  case 'YUY2': // Uncompressed YUV 4:2:2
838  $thisfile_video['lossless'] = true;
839  break;
840 
841  default:
842  $thisfile_video['lossless'] = false;
843  break;
844  }
845 
846  switch ($strhfccType) {
847  case 'vids':
848  $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
849  $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
850 
851  if ($thisfile_riff_video_current['codec'] == 'DV') {
852  $thisfile_riff_video_current['dv_type'] = 2;
853  }
854  break;
855 
856  case 'iavs':
857  $thisfile_riff_video_current['dv_type'] = 1;
858  break;
859  }
860  break;
861 
862  default:
863  $this->warning('Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"');
864  break;
865 
866  }
867  }
868  }
869 
870  if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
871 
872  $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
873  if (self::fourccLookup($thisfile_video['fourcc'])) {
874  $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
875  $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
876  }
877 
878  switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
879  case 'HFYU': // Huffman Lossless Codec
880  case 'IRAW': // Intel YUV Uncompressed
881  case 'YUY2': // Uncompressed YUV 4:2:2
882  $thisfile_video['lossless'] = true;
883  //$thisfile_video['bits_per_sample'] = 24;
884  break;
885 
886  default:
887  $thisfile_video['lossless'] = false;
888  //$thisfile_video['bits_per_sample'] = 24;
889  break;
890  }
891 
892  }
893  }
894  }
895  }
896  break;
897 
898 
899  case 'AMV ':
900  $info['fileformat'] = 'amv';
901  $info['mime_type'] = 'video/amv';
902 
903  $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
904  $thisfile_video['dataformat'] = 'mjpeg';
905  $thisfile_video['codec'] = 'mjpeg';
906  $thisfile_video['lossless'] = false;
907  $thisfile_video['bits_per_sample'] = 24;
908 
909  $thisfile_audio['dataformat'] = 'adpcm';
910  $thisfile_audio['lossless'] = false;
911  break;
912 
913 
914  // http://en.wikipedia.org/wiki/CD-DA
915  case 'CDDA':
916  $info['fileformat'] = 'cda';
917  unset($info['mime_type']);
918 
919  $thisfile_audio_dataformat = 'cda';
920 
921  $info['avdataoffset'] = 44;
922 
923  if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
924  // shortcut
925  $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
926 
927  $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
928  $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
929  $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
930  $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
931  $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
932  $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
933  $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
934 
935  $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
936  $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
937  $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
938  $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
939 
940  // hardcoded data for CD-audio
941  $thisfile_audio['lossless'] = true;
942  $thisfile_audio['sample_rate'] = 44100;
943  $thisfile_audio['channels'] = 2;
944  $thisfile_audio['bits_per_sample'] = 16;
945  $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
946  $thisfile_audio['bitrate_mode'] = 'cbr';
947  }
948  break;
949 
950  // http://en.wikipedia.org/wiki/AIFF
951  case 'AIFF':
952  case 'AIFC':
953  $info['fileformat'] = 'aiff';
954  $info['mime_type'] = 'audio/x-aiff';
955 
956  $thisfile_audio['bitrate_mode'] = 'cbr';
957  $thisfile_audio_dataformat = 'aiff';
958  $thisfile_audio['lossless'] = true;
959 
960  if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
961  $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
962  $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
963  if ($info['avdataend'] > $info['filesize']) {
964  if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
965  // structures rounded to 2-byte boundary, but dumb encoders
966  // forget to pad end of file to make this actually work
967  } else {
968  $this->warning('Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found');
969  }
970  $info['avdataend'] = $info['filesize'];
971  }
972  }
973 
974  if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
975 
976  // shortcut
977  $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
978 
979  $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
980  $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
981  $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
982  $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
983 
984  if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
985  $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
986  $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
987  $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
988  switch ($thisfile_riff_audio['codec_name']) {
989  case 'NONE':
990  $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
991  $thisfile_audio['lossless'] = true;
992  break;
993 
994  case '':
995  switch ($thisfile_riff_audio['codec_fourcc']) {
996  // http://developer.apple.com/qa/snd/snd07.html
997  case 'sowt':
998  $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
999  $thisfile_audio['lossless'] = true;
1000  break;
1001 
1002  case 'twos':
1003  $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
1004  $thisfile_audio['lossless'] = true;
1005  break;
1006 
1007  default:
1008  break;
1009  }
1010  break;
1011 
1012  default:
1013  $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
1014  $thisfile_audio['lossless'] = false;
1015  break;
1016  }
1017  }
1018 
1019  $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
1020  if ($thisfile_riff_audio['bits_per_sample'] > 0) {
1021  $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
1022  }
1023  $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
1024  if ($thisfile_audio['sample_rate'] == 0) {
1025  $this->error('Corrupted AIFF file: sample_rate == zero');
1026  return false;
1027  }
1028  $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
1029  }
1030 
1031  if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
1032  $offset = 0;
1033  $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1034  $offset += 2;
1035  for ($i = 0; $i < $CommentCount; $i++) {
1036  $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
1037  $offset += 4;
1038  $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1039  $offset += 2;
1040  $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1041  $offset += 2;
1042  $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1043  $offset += $CommentLength;
1044 
1045  $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1046  $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1047  }
1048  }
1049 
1050  $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1051  foreach ($CommentsChunkNames as $key => $value) {
1052  if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1053  $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1054  }
1055  }
1056 /*
1057  if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1058  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1059  $getid3_temp = new getID3();
1060  $getid3_temp->openfile($this->getid3->filename);
1061  $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1062  $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1063  if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1064  $info['id3v2'] = $getid3_temp->info['id3v2'];
1065  }
1066  unset($getid3_temp, $getid3_id3v2);
1067  }
1068 */
1069  break;
1070 
1071  // http://en.wikipedia.org/wiki/8SVX
1072  case '8SVX':
1073  $info['fileformat'] = '8svx';
1074  $info['mime_type'] = 'audio/8svx';
1075 
1076  $thisfile_audio['bitrate_mode'] = 'cbr';
1077  $thisfile_audio_dataformat = '8svx';
1078  $thisfile_audio['bits_per_sample'] = 8;
1079  $thisfile_audio['channels'] = 1; // overridden below, if need be
1080 
1081  if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1082  $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1083  $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1084  if ($info['avdataend'] > $info['filesize']) {
1085  $this->warning('Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found');
1086  }
1087  }
1088 
1089  if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1090  // shortcut
1091  $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1092 
1093  $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1094  $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1095  $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1096  $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1097  $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1098  $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1099  $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1100 
1101  $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1102 
1103  switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1104  case 0:
1105  $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1106  $thisfile_audio['lossless'] = true;
1107  $ActualBitsPerSample = 8;
1108  break;
1109 
1110  case 1:
1111  $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1112  $thisfile_audio['lossless'] = false;
1113  $ActualBitsPerSample = 4;
1114  break;
1115 
1116  default:
1117  $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"');
1118  break;
1119  }
1120  }
1121 
1122  if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1123  $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1124  switch ($ChannelsIndex) {
1125  case 6: // Stereo
1126  $thisfile_audio['channels'] = 2;
1127  break;
1128 
1129  case 2: // Left channel only
1130  case 4: // Right channel only
1131  $thisfile_audio['channels'] = 1;
1132  break;
1133 
1134  default:
1135  $this->warning('Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"');
1136  break;
1137  }
1138 
1139  }
1140 
1141  $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1142  foreach ($CommentsChunkNames as $key => $value) {
1143  if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1144  $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1145  }
1146  }
1147 
1148  $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1149  if (!empty($thisfile_audio['bitrate'])) {
1150  $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1151  }
1152  break;
1153 
1154  case 'CDXA':
1155  $info['fileformat'] = 'vcd'; // Asume Video CD
1156  $info['mime_type'] = 'video/mpeg';
1157 
1158  if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1159  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
1160 
1161  $getid3_temp = new getID3();
1162  $getid3_temp->openfile($this->getid3->filename);
1163  $getid3_mpeg = new getid3_mpeg($getid3_temp);
1164  $getid3_mpeg->Analyze();
1165  if (empty($getid3_temp->info['error'])) {
1166  $info['audio'] = $getid3_temp->info['audio'];
1167  $info['video'] = $getid3_temp->info['video'];
1168  $info['mpeg'] = $getid3_temp->info['mpeg'];
1169  $info['warning'] = $getid3_temp->info['warning'];
1170  }
1171  unset($getid3_temp, $getid3_mpeg);
1172  }
1173  break;
1174 
1175  case 'WEBP':
1176  // https://developers.google.com/speed/webp/docs/riff_container
1177  $info['fileformat'] = 'webp';
1178  $info['mime_type'] = 'image/webp';
1179 
1180 $this->error('WebP image parsing not supported in this version of getID3()');
1181  break;
1182 
1183  default:
1184  $this->error('Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA|WEBP), found "'.$RIFFsubtype.'" instead');
1185  //unset($info['fileformat']);
1186  }
1187 
1188  switch ($RIFFsubtype) {
1189  case 'WAVE':
1190  case 'AIFF':
1191  case 'AIFC':
1192  $ID3v2_key_good = 'id3 ';
1193  $ID3v2_keys_bad = array('ID3 ', 'tag ');
1194  foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1195  if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1196  $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1197  $this->warning('mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"');
1198  }
1199  }
1200 
1201  if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1202  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1203 
1204  $getid3_temp = new getID3();
1205  $getid3_temp->openfile($this->getid3->filename);
1206  $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1207  $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1208  if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1209  $info['id3v2'] = $getid3_temp->info['id3v2'];
1210  }
1211  unset($getid3_temp, $getid3_id3v2);
1212  }
1213  break;
1214  }
1215 
1216  if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1217  $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1218  }
1219  if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1220  self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1221  }
1222  if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1223  self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1224  }
1225 
1226  if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1227  $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1228  }
1229 
1230  if (!isset($info['playtime_seconds'])) {
1231  $info['playtime_seconds'] = 0;
1232  }
1233  if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1234  // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1235  $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1236  } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1237  $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1238  }
1239 
1240  if ($info['playtime_seconds'] > 0) {
1241  if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1242 
1243  if (!isset($info['bitrate'])) {
1244  $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1245  }
1246 
1247  } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1248 
1249  if (!isset($thisfile_audio['bitrate'])) {
1250  $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1251  }
1252 
1253  } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1254 
1255  if (!isset($thisfile_video['bitrate'])) {
1256  $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1257  }
1258 
1259  }
1260  }
1261 
1262 
1263  if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1264 
1265  $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1266  $thisfile_audio['bitrate'] = 0;
1267  $thisfile_video['bitrate'] = $info['bitrate'];
1268  foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1269  $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1270  $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1271  }
1272  if ($thisfile_video['bitrate'] <= 0) {
1273  unset($thisfile_video['bitrate']);
1274  }
1275  if ($thisfile_audio['bitrate'] <= 0) {
1276  unset($thisfile_audio['bitrate']);
1277  }
1278  }
1279 
1280  if (isset($info['mpeg']['audio'])) {
1281  $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1282  $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1283  $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1284  $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1285  $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1286  if (!empty($info['mpeg']['audio']['codec'])) {
1287  $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1288  }
1289  if (!empty($thisfile_audio['streams'])) {
1290  foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1291  if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1292  $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1293  $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1294  $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1295  $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1296  $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1297  }
1298  }
1299  }
1300  $getid3_mp3 = new getid3_mp3($this->getid3);
1301  $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1302  unset($getid3_mp3);
1303  }
1304 
1305 
1306  if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1307  switch ($thisfile_audio_dataformat) {
1308  case 'ac3':
1309  // ignore bits_per_sample
1310  break;
1311 
1312  default:
1313  $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1314  break;
1315  }
1316  }
1317 
1318 
1319  if (empty($thisfile_riff_raw)) {
1320  unset($thisfile_riff['raw']);
1321  }
1322  if (empty($thisfile_riff_audio)) {
1323  unset($thisfile_riff['audio']);
1324  }
1325  if (empty($thisfile_riff_video)) {
1326  unset($thisfile_riff['video']);
1327  }
1328 
1329  return true;
1330  }
1331 
1332  public function ParseRIFFAMV($startoffset, $maxoffset) {
1333  // 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
1334 
1335  // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
1336  //typedef struct _amvmainheader {
1337  //FOURCC fcc; // 'amvh'
1338  //DWORD cb;
1339  //DWORD dwMicroSecPerFrame;
1340  //BYTE reserve[28];
1341  //DWORD dwWidth;
1342  //DWORD dwHeight;
1343  //DWORD dwSpeed;
1344  //DWORD reserve0;
1345  //DWORD reserve1;
1346  //BYTE bTimeSec;
1347  //BYTE bTimeMin;
1348  //WORD wTimeHour;
1349  //} AMVMAINHEADER;
1350 
1351  $info = &$this->getid3->info;
1352  $RIFFchunk = false;
1353 
1354  try {
1355 
1356  $this->fseek($startoffset);
1357  $maxoffset = min($maxoffset, $info['avdataend']);
1358  $AMVheader = $this->fread(284);
1359  if (substr($AMVheader, 0, 8) != 'hdrlamvh') {
1360  throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"');
1361  }
1362  if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
1363  throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"');
1364  }
1365  $RIFFchunk = array();
1366  $RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4));
1367  $RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved?
1368  $RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4));
1369  $RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4));
1370  $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4));
1371  $RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved?
1372  $RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved?
1373  $RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1));
1374  $RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1));
1375  $RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2));
1376 
1377  $info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
1378  $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
1379  $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
1380  $info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
1381 
1382  // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded
1383 
1384  if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
1385  throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"');
1386  }
1387  // followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144
1388  if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") {
1389  throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"');
1390  }
1391  // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
1392 
1393  if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
1394  throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
1395  }
1396  // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
1397  if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") {
1398  throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"');
1399  }
1400  // followed by 20 bytes of a modified WAVEFORMATEX:
1401  // typedef struct {
1402  // WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code)
1403  // WORD nChannels; //(Fixme: this is always 1)
1404  // DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050)
1405  // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
1406  // WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?)
1407  // WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
1408  // WORD cbSize; //(Fixme: this seems to be 0 in AMV files)
1409  // WORD reserved;
1410  // } WAVEFORMATEX;
1411  $RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2));
1412  $RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2));
1413  $RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4));
1414  $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4));
1415  $RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2));
1416  $RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2));
1417  $RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2));
1418  $RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2));
1419 
1420 
1421  $info['audio']['lossless'] = false;
1422  $info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec'];
1423  $info['audio']['channels'] = $RIFFchunk['strf']['nchannels'];
1424  $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
1425  $info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
1426  $info['audio']['bitrate_mode'] = 'cbr';
1427 
1428 
1429  } catch (getid3_exception $e) {
1430  if ($e->getCode() == 10) {
1431  $this->warning('RIFFAMV parser: '.$e->getMessage());
1432  } else {
1433  throw $e;
1434  }
1435  }
1436 
1437  return $RIFFchunk;
1438  }
1439 
1440 
1441  public function ParseRIFF($startoffset, $maxoffset) {
1442  $info = &$this->getid3->info;
1443 
1444  $RIFFchunk = false;
1445  $FoundAllChunksWeNeed = false;
1446 
1447  try {
1448  $this->fseek($startoffset);
1449  $maxoffset = min($maxoffset, $info['avdataend']);
1450  while ($this->ftell() < $maxoffset) {
1451  $chunknamesize = $this->fread(8);
1452  //$chunkname = substr($chunknamesize, 0, 4);
1453  $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
1454  $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1455  //if (strlen(trim($chunkname, "\x00")) < 4) {
1456  if (strlen($chunkname) < 4) {
1457  $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1458  break;
1459  }
1460  if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1461  $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1462  break;
1463  }
1464  if (($chunksize % 2) != 0) {
1465  // all structures are packed on word boundaries
1466  $chunksize++;
1467  }
1468 
1469  switch ($chunkname) {
1470  case 'LIST':
1471  $listname = $this->fread(4);
1472  if (preg_match('#^(movi|rec )$#i', $listname)) {
1473  $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1474  $RIFFchunk[$listname]['size'] = $chunksize;
1475 
1476  if (!$FoundAllChunksWeNeed) {
1477  $WhereWeWere = $this->ftell();
1478  $AudioChunkHeader = $this->fread(12);
1479  $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1480  $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1481  $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1482 
1483  if ($AudioChunkStreamType == 'wb') {
1484  $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1485  if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1486  // MP3
1487  if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1488  $getid3_temp = new getID3();
1489  $getid3_temp->openfile($this->getid3->filename);
1490  $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1491  $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1492  $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1493  $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1494  if (isset($getid3_temp->info['mpeg']['audio'])) {
1495  $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1496  $info['audio'] = $getid3_temp->info['audio'];
1497  $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1498  $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1499  $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1500  $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1501  $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1502  //$info['bitrate'] = $info['audio']['bitrate'];
1503  }
1504  unset($getid3_temp, $getid3_mp3);
1505  }
1506 
1507  } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1508 
1509  // AC3
1510  $getid3_temp = new getID3();
1511  $getid3_temp->openfile($this->getid3->filename);
1512  $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1513  $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1514  $getid3_ac3 = new getid3_ac3($getid3_temp);
1515  $getid3_ac3->Analyze();
1516  if (empty($getid3_temp->info['error'])) {
1517  $info['audio'] = $getid3_temp->info['audio'];
1518  $info['ac3'] = $getid3_temp->info['ac3'];
1519  if (!empty($getid3_temp->info['warning'])) {
1520  foreach ($getid3_temp->info['warning'] as $key => $value) {
1521  $this->warning($value);
1522  }
1523  }
1524  }
1525  unset($getid3_temp, $getid3_ac3);
1526  }
1527  }
1528  $FoundAllChunksWeNeed = true;
1529  $this->fseek($WhereWeWere);
1530  }
1531  $this->fseek($chunksize - 4, SEEK_CUR);
1532 
1533  } else {
1534 
1535  if (!isset($RIFFchunk[$listname])) {
1536  $RIFFchunk[$listname] = array();
1537  }
1538  $LISTchunkParent = $listname;
1539  $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1540  if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1541  $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1542  }
1543 
1544  }
1545  break;
1546 
1547  default:
1548  if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1549  $this->fseek($chunksize, SEEK_CUR);
1550  break;
1551  }
1552  $thisindex = 0;
1553  if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1554  $thisindex = count($RIFFchunk[$chunkname]);
1555  }
1556  $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1557  $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1558  switch ($chunkname) {
1559  case 'data':
1560  $info['avdataoffset'] = $this->ftell();
1561  $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1562 
1563  $testData = $this->fread(36);
1564  if ($testData === '') {
1565  break;
1566  }
1567  if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1568 
1569  // Probably is MP3 data
1570  if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1571  $getid3_temp = new getID3();
1572  $getid3_temp->openfile($this->getid3->filename);
1573  $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1574  $getid3_temp->info['avdataend'] = $info['avdataend'];
1575  $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1576  $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1577  if (empty($getid3_temp->info['error'])) {
1578  $info['audio'] = $getid3_temp->info['audio'];
1579  $info['mpeg'] = $getid3_temp->info['mpeg'];
1580  }
1581  unset($getid3_temp, $getid3_mp3);
1582  }
1583 
1584  } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1585 
1586  // This is probably AC-3 data
1587  $getid3_temp = new getID3();
1588  if ($isRegularAC3) {
1589  $getid3_temp->openfile($this->getid3->filename);
1590  $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1591  $getid3_temp->info['avdataend'] = $info['avdataend'];
1592  }
1593  $getid3_ac3 = new getid3_ac3($getid3_temp);
1594  if ($isRegularAC3) {
1595  $getid3_ac3->Analyze();
1596  } else {
1597  // Dolby Digital WAV
1598  // AC-3 content, but not encoded in same format as normal AC-3 file
1599  // For one thing, byte order is swapped
1600  $ac3_data = '';
1601  for ($i = 0; $i < 28; $i += 2) {
1602  $ac3_data .= substr($testData, 8 + $i + 1, 1);
1603  $ac3_data .= substr($testData, 8 + $i + 0, 1);
1604  }
1605  $getid3_ac3->AnalyzeString($ac3_data);
1606  }
1607 
1608  if (empty($getid3_temp->info['error'])) {
1609  $info['audio'] = $getid3_temp->info['audio'];
1610  $info['ac3'] = $getid3_temp->info['ac3'];
1611  if (!empty($getid3_temp->info['warning'])) {
1612  foreach ($getid3_temp->info['warning'] as $newerror) {
1613  $this->warning('getid3_ac3() says: ['.$newerror.']');
1614  }
1615  }
1616  }
1617  unset($getid3_temp, $getid3_ac3);
1618 
1619  } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1620 
1621  // This is probably DTS data
1622  $getid3_temp = new getID3();
1623  $getid3_temp->openfile($this->getid3->filename);
1624  $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1625  $getid3_dts = new getid3_dts($getid3_temp);
1626  $getid3_dts->Analyze();
1627  if (empty($getid3_temp->info['error'])) {
1628  $info['audio'] = $getid3_temp->info['audio'];
1629  $info['dts'] = $getid3_temp->info['dts'];
1630  $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1631  if (!empty($getid3_temp->info['warning'])) {
1632  foreach ($getid3_temp->info['warning'] as $newerror) {
1633  $this->warning('getid3_dts() says: ['.$newerror.']');
1634  }
1635  }
1636  }
1637 
1638  unset($getid3_temp, $getid3_dts);
1639 
1640  } elseif (substr($testData, 0, 4) == 'wvpk') {
1641 
1642  // This is WavPack data
1643  $info['wavpack']['offset'] = $info['avdataoffset'];
1644  $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1645  $this->parseWavPackHeader(substr($testData, 8, 28));
1646 
1647  } else {
1648  // This is some other kind of data (quite possibly just PCM)
1649  // do nothing special, just skip it
1650  }
1651  $nextoffset = $info['avdataend'];
1652  $this->fseek($nextoffset);
1653  break;
1654 
1655  case 'iXML':
1656  case 'bext':
1657  case 'cart':
1658  case 'fmt ':
1659  case 'strh':
1660  case 'strf':
1661  case 'indx':
1662  case 'MEXT':
1663  case 'DISP':
1664  // always read data in
1665  case 'JUNK':
1666  // should be: never read data in
1667  // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1668  if ($chunksize < 1048576) {
1669  if ($chunksize > 0) {
1670  $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1671  if ($chunkname == 'JUNK') {
1672  if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1673  // only keep text characters [chr(32)-chr(127)]
1674  $info['riff']['comments']['junk'][] = trim($matches[1]);
1675  }
1676  // but if nothing there, ignore
1677  // remove the key in either case
1678  unset($RIFFchunk[$chunkname][$thisindex]['data']);
1679  }
1680  }
1681  } else {
1682  $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1683  $this->fseek($chunksize, SEEK_CUR);
1684  }
1685  break;
1686 
1687  //case 'IDVX':
1688  // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1689  // break;
1690 
1691  default:
1692  if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1693  $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1694  $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1695  unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1696  unset($RIFFchunk[$chunkname][$thisindex]['size']);
1697  if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1698  unset($RIFFchunk[$chunkname][$thisindex]);
1699  }
1700  if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1701  unset($RIFFchunk[$chunkname]);
1702  }
1703  $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1704  } elseif ($chunksize < 2048) {
1705  // only read data in if smaller than 2kB
1706  $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1707  } else {
1708  $this->fseek($chunksize, SEEK_CUR);
1709  }
1710  break;
1711  }
1712  break;
1713  }
1714  }
1715 
1716  } catch (getid3_exception $e) {
1717  if ($e->getCode() == 10) {
1718  $this->warning('RIFF parser: '.$e->getMessage());
1719  } else {
1720  throw $e;
1721  }
1722  }
1723 
1724  return $RIFFchunk;
1725  }
1726 
1727  public function ParseRIFFdata(&$RIFFdata) {
1728  $info = &$this->getid3->info;
1729  if ($RIFFdata) {
1730  $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1731  $fp_temp = fopen($tempfile, 'wb');
1732  $RIFFdataLength = strlen($RIFFdata);
1733  $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1734  for ($i = 0; $i < 4; $i++) {
1735  $RIFFdata[($i + 4)] = $NewLengthString[$i];
1736  }
1737  fwrite($fp_temp, $RIFFdata);
1738  fclose($fp_temp);
1739 
1740  $getid3_temp = new getID3();
1741  $getid3_temp->openfile($tempfile);
1742  $getid3_temp->info['filesize'] = $RIFFdataLength;
1743  $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1744  $getid3_temp->info['tags'] = $info['tags'];
1745  $getid3_temp->info['warning'] = $info['warning'];
1746  $getid3_temp->info['error'] = $info['error'];
1747  $getid3_temp->info['comments'] = $info['comments'];
1748  $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1749  $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1750  $getid3_riff = new getid3_riff($getid3_temp);
1751  $getid3_riff->Analyze();
1752 
1753  $info['riff'] = $getid3_temp->info['riff'];
1754  $info['warning'] = $getid3_temp->info['warning'];
1755  $info['error'] = $getid3_temp->info['error'];
1756  $info['tags'] = $getid3_temp->info['tags'];
1757  $info['comments'] = $getid3_temp->info['comments'];
1758  unset($getid3_riff, $getid3_temp);
1759  unlink($tempfile);
1760  }
1761  return false;
1762  }
1763 
1764  public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1765  $RIFFinfoKeyLookup = array(
1766  'IARL'=>'archivallocation',
1767  'IART'=>'artist',
1768  'ICDS'=>'costumedesigner',
1769  'ICMS'=>'commissionedby',
1770  'ICMT'=>'comment',
1771  'ICNT'=>'country',
1772  'ICOP'=>'copyright',
1773  'ICRD'=>'creationdate',
1774  'IDIM'=>'dimensions',
1775  'IDIT'=>'digitizationdate',
1776  'IDPI'=>'resolution',
1777  'IDST'=>'distributor',
1778  'IEDT'=>'editor',
1779  'IENG'=>'engineers',
1780  'IFRM'=>'accountofparts',
1781  'IGNR'=>'genre',
1782  'IKEY'=>'keywords',
1783  'ILGT'=>'lightness',
1784  'ILNG'=>'language',
1785  'IMED'=>'orignalmedium',
1786  'IMUS'=>'composer',
1787  'INAM'=>'title',
1788  'IPDS'=>'productiondesigner',
1789  'IPLT'=>'palette',
1790  'IPRD'=>'product',
1791  'IPRO'=>'producer',
1792  'IPRT'=>'part',
1793  'IRTD'=>'rating',
1794  'ISBJ'=>'subject',
1795  'ISFT'=>'software',
1796  'ISGN'=>'secondarygenre',
1797  'ISHP'=>'sharpness',
1798  'ISRC'=>'sourcesupplier',
1799  'ISRF'=>'digitizationsource',
1800  'ISTD'=>'productionstudio',
1801  'ISTR'=>'starring',
1802  'ITCH'=>'encoded_by',
1803  'IWEB'=>'url',
1804  'IWRI'=>'writer',
1805  '____'=>'comment',
1806  );
1807  foreach ($RIFFinfoKeyLookup as $key => $value) {
1808  if (isset($RIFFinfoArray[$key])) {
1809  foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1810  if (trim($commentdata['data']) != '') {
1811  if (isset($CommentsTargetArray[$value])) {
1812  $CommentsTargetArray[$value][] = trim($commentdata['data']);
1813  } else {
1814  $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1815  }
1816  }
1817  }
1818  }
1819  }
1820  return true;
1821  }
1822 
1823  public static function parseWAVEFORMATex($WaveFormatExData) {
1824  // shortcut
1825  $WaveFormatEx['raw'] = array();
1826  $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1827 
1828  $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2);
1829  $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2);
1830  $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4);
1831  $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4);
1832  $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2);
1833  $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2);
1834  if (strlen($WaveFormatExData) > 16) {
1835  $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2);
1836  }
1837  $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1838 
1839  $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1840  $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1841  $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1842  $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1843  $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1844 
1845  return $WaveFormatEx;
1846  }
1847 
1848  public function parseWavPackHeader($WavPackChunkData) {
1849  // typedef struct {
1850  // char ckID [4];
1851  // long ckSize;
1852  // short version;
1853  // short bits; // added for version 2.00
1854  // short flags, shift; // added for version 3.00
1855  // long total_samples, crc, crc2;
1856  // char extension [4], extra_bc, extras [3];
1857  // } WavpackHeader;
1858 
1859  // shortcut
1860  $info = &$this->getid3->info;
1861  $info['wavpack'] = array();
1862  $thisfile_wavpack = &$info['wavpack'];
1863 
1864  $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1865  if ($thisfile_wavpack['version'] >= 2) {
1866  $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1867  }
1868  if ($thisfile_wavpack['version'] >= 3) {
1869  $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1870  $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1871  $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1872  $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1873  $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1874  $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1875  $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1876  for ($i = 0; $i <= 2; $i++) {
1877  $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1878  }
1879 
1880  // shortcut
1881  $thisfile_wavpack['flags'] = array();
1882  $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1883 
1884  $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1885  $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1886  $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1887  $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1888  $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1889  $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1890  $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1891  $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1892  $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1893  $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1894  $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1895  $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1896  $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1897  $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1898  $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1899  $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1900  $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1901  $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1902  $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1903  $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1904  }
1905 
1906  return true;
1907  }
1908 
1909  public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1910 
1911  $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1912  $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels
1913  $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
1914  $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1915  $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1916  $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1917  $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1918  $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1919  $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
1920  $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
1921  $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1922 
1923  $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1924 
1925  return $parsed;
1926  }
1927 
1928  public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1929  // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1930  // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1931  // 'Byte Layout: '1111111111111111
1932  // '32 for Movie - 1 '1111111111111111
1933  // '28 for Author - 6 '6666666666666666
1934  // '4 for year - 2 '6666666666662222
1935  // '3 for genre - 3 '7777777777777777
1936  // '48 for Comments - 7 '7777777777777777
1937  // '1 for Rating - 4 '7777777777777777
1938  // '5 for Future Additions - 0 '333400000DIVXTAG
1939  // '128 bytes total
1940 
1941  static $DIVXTAGgenre = array(
1942  0 => 'Action',
1943  1 => 'Action/Adventure',
1944  2 => 'Adventure',
1945  3 => 'Adult',
1946  4 => 'Anime',
1947  5 => 'Cartoon',
1948  6 => 'Claymation',
1949  7 => 'Comedy',
1950  8 => 'Commercial',
1951  9 => 'Documentary',
1952  10 => 'Drama',
1953  11 => 'Home Video',
1954  12 => 'Horror',
1955  13 => 'Infomercial',
1956  14 => 'Interactive',
1957  15 => 'Mystery',
1958  16 => 'Music Video',
1959  17 => 'Other',
1960  18 => 'Religion',
1961  19 => 'Sci Fi',
1962  20 => 'Thriller',
1963  21 => 'Western',
1964  ),
1965  $DIVXTAGrating = array(
1966  0 => 'Unrated',
1967  1 => 'G',
1968  2 => 'PG',
1969  3 => 'PG-13',
1970  4 => 'R',
1971  5 => 'NC-17',
1972  );
1973 
1974  $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1975  $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1976  $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1977  $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1978  $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1979  $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1980  //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1981  //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1982 
1983  $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1984  $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1985 
1986  if (!$raw) {
1987  unset($parsed['genre_id'], $parsed['rating_id']);
1988  foreach ($parsed as $key => $value) {
1989  if (!$value === '') {
1990  unset($parsed['key']);
1991  }
1992  }
1993  }
1994 
1995  foreach ($parsed as $tag => $value) {
1996  $parsed[$tag] = array($value);
1997  }
1998 
1999  return $parsed;
2000  }
2001 
2002  public static function waveSNDMtagLookup($tagshortname) {
2003  $begin = __LINE__;
2004 
2022  return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
2023  }
2024 
2025  public static function wFormatTagLookup($wFormatTag) {
2026 
2027  $begin = __LINE__;
2028 
2191  return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2192  }
2193 
2194  public static function fourccLookup($fourcc) {
2195 
2196  $begin = __LINE__;
2197 
2585  return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
2586  }
2587 
2588  private function EitherEndian2Int($byteword, $signed=false) {
2589  if ($this->container == 'riff') {
2590  return getid3_lib::LittleEndian2Int($byteword, $signed);
2591  }
2592  return getid3_lib::BigEndian2Int($byteword, false, $signed);
2593  }
2594 
2595 }
static $syncwords
Possible syncwords indicating bitstream encoding.
ParseRIFFAMV($startoffset, $maxoffset)
ParseRIFF($startoffset, $maxoffset)
static ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true)
$h
error($text)
Definition: getid3.php:1752
static EmbeddedLookup($key, $begin, $end, $file, $name)
static RGADnameLookup($namecode)
static Dec2Bin($number)
Definition: getid3.lib.php:328
static waveSNDMtagLookup($tagshortname)
static RGADoriginatorLookup($originatorcode)
static MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false)
warning($text)
Definition: getid3.php:1758
static Bin2Dec($binstring, $signed=false)
Definition: getid3.lib.php:342
$s
Definition: pwgen.php:45
static parseWAVEFORMATex($WaveFormatExData)
static LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:292
static IncludeDependency($filename, $sourcefile, $DieOnFailure=false)
static fourccLookup($fourcc)
static BigEndian2Float($byteword)
Definition: getid3.lib.php:185
static RGADadjustmentLookup($rawadjustment, $signbit)
static XML2array($XMLstring)
Definition: getid3.lib.php:535
EitherEndian2Int($byteword, $signed=false)
parseWavPackHeader($WavPackChunkData)
static DateMac2Unix($macdate)
Definition: getid3.lib.php:468
static CastAsInt($floatnum)
Definition: getid3.lib.php:65
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: getid3.lib.php:18
fread($bytes)
Definition: getid3.php:1683
static ParseDIVXTAG($DIVXTAG, $raw=false)
static LittleEndian2Float($byteword)
Definition: getid3.lib.php:180
static parseComments(&$RIFFinfoArray, &$CommentsTargetArray)
$i
Definition: disco.tpl.php:19
static wFormatTagLookup($wFormatTag)
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
$info
Definition: index.php:5
static LittleEndian2String($number, $minbytes=1, $synchsafe=false)
Definition: getid3.lib.php:369
$key
Definition: croninfo.php:18
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:263
if(function_exists('posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
getID3() by James Heinrich info@getid3.org //
getID3() by James Heinrich info@getid3.org //
static FixedPoint16_16($rawdata)
Definition: getid3.lib.php:480
static array_merge_noclobber($array1, $array2)
Definition: getid3.lib.php:402