ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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
26getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29
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}
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
getID3() by James Heinrich info@getid3.org //
getID3() by James Heinrich info@getid3.org //
static $syncwords
Possible syncwords indicating bitstream encoding.
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
fread($bytes)
Definition: getid3.php:1683
warning($text)
Definition: getid3.php:1758
error($text)
Definition: getid3.php:1752
static RGADadjustmentLookup($rawadjustment, $signbit)
static XML2array($XMLstring)
Definition: getid3.lib.php:535
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: getid3.lib.php:18
static LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:292
static FixedPoint16_16($rawdata)
Definition: getid3.lib.php:480
static LittleEndian2String($number, $minbytes=1, $synchsafe=false)
Definition: getid3.lib.php:369
static RGADoriginatorLookup($originatorcode)
static LittleEndian2Float($byteword)
Definition: getid3.lib.php:180
static Bin2Dec($binstring, $signed=false)
Definition: getid3.lib.php:342
static DateMac2Unix($macdate)
Definition: getid3.lib.php:468
static IncludeDependency($filename, $sourcefile, $DieOnFailure=false)
static array_merge_noclobber($array1, $array2)
Definition: getid3.lib.php:402
static RGADnameLookup($namecode)
static BigEndian2Float($byteword)
Definition: getid3.lib.php:185
static EmbeddedLookup($key, $begin, $end, $file, $name)
static Dec2Bin($number)
Definition: getid3.lib.php:328
static CastAsInt($floatnum)
Definition: getid3.lib.php:65
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: getid3.lib.php:263
static MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false)
static parseComments(&$RIFFinfoArray, &$CommentsTargetArray)
static parseWAVEFORMATex($WaveFormatExData)
parseWavPackHeader($WavPackChunkData)
EitherEndian2Int($byteword, $signed=false)
ParseRIFF($startoffset, $maxoffset)
static wFormatTagLookup($wFormatTag)
static ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true)
static waveSNDMtagLookup($tagshortname)
static fourccLookup($fourcc)
static ParseDIVXTAG($DIVXTAG, $raw=false)
ParseRIFFAMV($startoffset, $maxoffset)
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$h
if(function_exists( 'posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
$info
Definition: index.php:5
$s
Definition: pwgen.php:45