2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org> //
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
7 /////////////////////////////////////////////////////////////////
8 // See readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
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 //
19 /////////////////////////////////////////////////////////////////
22 * @todo Parse AC-3/DTS audio inside WAVE correctly
23 * @todo Rewrite RIFF parser totally
26 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
30 class getid3_riff extends getid3_handler {
32 protected $container = 'riff'; // default
34 public function Analyze() {
35 $info = &$this->getid3->info;
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());
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'];
50 $Original['avdataoffset'] = $info['avdataoffset'];
51 $Original['avdataend'] = $info['avdataend'];
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);
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));
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';
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));
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'];
91 $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
92 while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
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');
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
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'];
120 switch ($nextRIFFheaderID) {
122 $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
123 if (!isset($thisfile_riff[$nextRIFFtype])) {
124 $thisfile_riff[$nextRIFFtype] = array();
126 $thisfile_riff[$nextRIFFtype][] = $chunkdata;
130 unset($info['riff']);
131 $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
136 $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
140 $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
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);
153 $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
159 if ($RIFFsubtype == 'WAVE') {
160 $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
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']);
171 switch ($RIFFsubtype) {
173 // http://en.wikipedia.org/wiki/Wav
175 $info['fileformat'] = 'wav';
177 if (empty($thisfile_audio['bitrate_mode'])) {
178 $thisfile_audio['bitrate_mode'] = 'cbr';
180 if (empty($thisfile_audio_dataformat)) {
181 $thisfile_audio_dataformat = 'wav';
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'];
188 if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
190 $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
191 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
192 if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
193 $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
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];
200 $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
201 if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
202 $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
204 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
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']);
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']) {
215 $thisfile_audio['lossless'] = true;
219 $thisfile_audio_dataformat = 'ac3';
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;
234 if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
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'];
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));
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));
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']);
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']);
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));
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
278 // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
279 // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
282 if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
283 $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
286 if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
288 $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
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']);
305 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
308 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
310 $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
311 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
314 if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
316 $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
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);
325 $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
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);
334 if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
336 $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
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));
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)));
361 $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
362 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
365 if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
366 // SoundMiner metadata
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'];
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;
387 if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
388 $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
390 } elseif ($SNDM_thisTagSize <= 0) {
391 $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
394 $SNDM_startoffset += $SNDM_thisTagSize;
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;
400 $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
405 'tracktitle'=>'title',
406 'category' =>'genre',
408 'tracktitle'=>'title',
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];
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);
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);
429 if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
430 $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
431 $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
432 $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
433 $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60);
434 $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
435 $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
436 $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
437 $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
445 if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
446 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
447 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
450 if (!empty($info['wavpack'])) {
451 $thisfile_audio_dataformat = 'wavpack';
452 $thisfile_audio['bitrate_mode'] = 'vbr';
453 $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version'];
455 // Reset to the way it was - RIFF parsing will have messed this up
456 $info['avdataend'] = $Original['avdataend'];
457 $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
459 $this->fseek($info['avdataoffset'] - 44);
460 $RIFFdata = $this->fread(44);
461 $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
462 $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
464 if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
465 $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
466 $this->fseek($info['avdataend']);
467 $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
470 // move the data chunk after all other chunks (if any)
471 // so that the RIFF parser doesn't see EOF when trying
472 // to skip over the data chunk
473 $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
474 $getid3_riff = new getid3_riff($this->getid3);
475 $getid3_riff->ParseRIFFdata($RIFFdata);
479 if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
480 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
482 if (!empty($info['ac3'])) {
483 // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
484 $thisfile_audio['wformattag'] = 0x2000;
485 $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
486 $thisfile_audio['lossless'] = false;
487 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
488 $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
490 if (!empty($info['dts'])) {
491 // Dolby DTS files masquerade as PCM-WAV, but they're not
492 $thisfile_audio['wformattag'] = 0x2001;
493 $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
494 $thisfile_audio['lossless'] = false;
495 $thisfile_audio['bitrate'] = $info['dts']['bitrate'];
496 $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
499 case 0x08AE: // ClearJump LiteWave
500 $thisfile_audio['bitrate_mode'] = 'vbr';
501 $thisfile_audio_dataformat = 'litewave';
503 //typedef struct tagSLwFormat {
504 // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags
505 // DWORD m_dwScale; // scale factor for lossy compression
506 // DWORD m_dwBlockSize; // number of samples in encoded blocks
507 // WORD m_wQuality; // alias for the scale factor
508 // WORD m_wMarkDistance; // distance between marks in bytes
511 // //following paramters are ignored if CF_FILESRC is not set
512 // DWORD m_dwOrgSize; // original file size in bytes
513 // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file
514 // DWORD m_dwRiffChunkSize; // riff chunk size in the original file
516 // PCMWAVEFORMAT m_OrgWf; // original wave format
517 // }SLwFormat, *PSLwFormat;
520 $thisfile_riff['litewave']['raw'] = array();
521 $riff_litewave = &$thisfile_riff['litewave'];
522 $riff_litewave_raw = &$riff_litewave['raw'];
525 'compression_method' => 1,
526 'compression_flags' => 1,
528 'm_dwBlockSize' => 4,
530 'm_wMarkDistance' => 2,
533 'm_bFactExists' => 2,
534 'm_dwRiffChunkSize' => 4,
536 $litewave_offset = 18;
537 foreach ($flags as $flag => $length) {
538 $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
539 $litewave_offset += $length;
542 //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
543 $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
545 $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
546 $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
547 $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
549 $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
550 $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
557 if ($info['avdataend'] > $info['filesize']) {
558 switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
559 case 'wavpack': // WavPack
561 case 'ofr': // OptimFROG
562 case 'ofs': // OptimFROG DualStream
563 // lossless compressed audio formats that keep original RIFF headers - skip warning
567 if (($info['avdataend'] - $info['filesize']) == 1) {
568 // LiteWave appears to incorrectly *not* pad actual output file
569 // to nearest WORD boundary so may appear to be short by one
570 // byte, in which case - skip warning
572 // Short by more than one byte, throw warning
573 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
574 $info['avdataend'] = $info['filesize'];
579 if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
580 // output file appears to be incorrectly *not* padded to nearest WORD boundary
581 // Output less severe warning
582 $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
583 $info['avdataend'] = $info['filesize'];
585 // Short by more than one byte, throw warning
586 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
587 $info['avdataend'] = $info['filesize'];
592 if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
593 if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
594 $info['avdataend']--;
595 $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
598 if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
599 unset($thisfile_audio['bits_per_sample']);
600 if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
601 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
606 // http://en.wikipedia.org/wiki/Audio_Video_Interleave
608 $info['fileformat'] = 'avi';
609 $info['mime_type'] = 'video/avi';
611 $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
612 $thisfile_video['dataformat'] = 'avi';
614 if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
615 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
616 if (isset($thisfile_riff['AVIX'])) {
617 $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
619 $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
621 if ($info['avdataend'] > $info['filesize']) {
622 $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
623 $info['avdataend'] = $info['filesize'];
627 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
628 //$bIndexType = array(
629 // 0x00 => 'AVI_INDEX_OF_INDEXES',
630 // 0x01 => 'AVI_INDEX_OF_CHUNKS',
631 // 0x80 => 'AVI_INDEX_IS_DATA',
633 //$bIndexSubtype = array(
635 // 0x01 => 'AVI_INDEX_2FIELD',
638 foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
639 $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
641 $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
642 $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
643 $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
644 $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
645 $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
646 $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
648 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
649 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
654 if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
655 $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
658 $thisfile_riff_raw['avih'] = array();
659 $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
661 $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L)
662 if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
663 $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
668 'dwMaxBytesPerSec', // max. transfer rate
669 'dwPaddingGranularity', // pad to multiples of this size; normally 2K.
670 'dwFlags', // the ever-present flags
671 'dwTotalFrames', // # frames in file
672 'dwInitialFrames', //
674 'dwSuggestedBufferSize', //
683 foreach ($flags as $flag) {
684 $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
689 'hasindex' => 0x00000010,
690 'mustuseindex' => 0x00000020,
691 'interleaved' => 0x00000100,
692 'trustcktype' => 0x00000800,
693 'capturedfile' => 0x00010000,
694 'copyrighted' => 0x00020010,
696 foreach ($flags as $flag => $value) {
697 $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
701 $thisfile_riff_video[$streamindex] = array();
702 $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
704 if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
705 $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
706 $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
708 if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
709 $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
710 $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
712 if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
713 $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
714 $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
717 $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
718 $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
720 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
721 if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
722 for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
723 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
724 $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
725 $strhfccType = substr($strhData, 0, 4);
727 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
728 $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
731 $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
733 switch ($strhfccType) {
735 $thisfile_audio['bitrate_mode'] = 'cbr';
736 $thisfile_audio_dataformat = 'wav';
737 if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
738 $streamindex = count($thisfile_riff_audio);
741 $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
742 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
745 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
746 $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
748 if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
749 unset($thisfile_audio_streams_currentstream['bits_per_sample']);
751 $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
752 unset($thisfile_audio_streams_currentstream['raw']);
755 $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
757 unset($thisfile_riff_audio[$streamindex]['raw']);
758 $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
760 $thisfile_audio['lossless'] = false;
761 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
763 $thisfile_audio_dataformat = 'wav';
764 $thisfile_audio['lossless'] = true;
767 case 0x0050: // MPEG Layer 2 or Layer 1
768 $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
771 case 0x0055: // MPEG Layer 3
772 $thisfile_audio_dataformat = 'mp3';
776 $thisfile_audio_dataformat = 'aac';
779 case 0x0161: // Windows Media v7 / v8 / v9
780 case 0x0162: // Windows Media Professional v9
781 case 0x0163: // Windows Media Lossess v9
782 $thisfile_audio_dataformat = 'wma';
786 $thisfile_audio_dataformat = 'ac3';
790 $thisfile_audio_dataformat = 'dts';
794 $thisfile_audio_dataformat = 'wav';
797 $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
798 $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
799 $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
806 $thisfile_riff_raw['strh'][$i] = array();
807 $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i];
809 $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType;
810 $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
811 $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags
812 $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
813 $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
814 $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
815 $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
816 $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
817 $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
818 $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
819 $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
820 $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
821 $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
822 $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
824 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
825 $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
826 if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
827 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
828 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
830 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
831 $thisfile_video['pixel_aspect_ratio'] = (float) 1;
832 switch ($thisfile_riff_raw_strh_current['fccHandler']) {
833 case 'HFYU': // Huffman Lossless Codec
834 case 'IRAW': // Intel YUV Uncompressed
835 case 'YUY2': // Uncompressed YUV 4:2:2
836 $thisfile_video['lossless'] = true;
840 $thisfile_video['lossless'] = false;
844 switch ($strhfccType) {
846 $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
847 $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
849 if ($thisfile_riff_video_current['codec'] == 'DV') {
850 $thisfile_riff_video_current['dv_type'] = 2;
855 $thisfile_riff_video_current['dv_type'] = 1;
861 $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
868 if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
870 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
871 if (self::fourccLookup($thisfile_video['fourcc'])) {
872 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
873 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
876 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
877 case 'HFYU': // Huffman Lossless Codec
878 case 'IRAW': // Intel YUV Uncompressed
879 case 'YUY2': // Uncompressed YUV 4:2:2
880 $thisfile_video['lossless'] = true;
881 //$thisfile_video['bits_per_sample'] = 24;
885 $thisfile_video['lossless'] = false;
886 //$thisfile_video['bits_per_sample'] = 24;
898 $info['fileformat'] = 'amv';
899 $info['mime_type'] = 'video/amv';
901 $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
902 $thisfile_video['dataformat'] = 'mjpeg';
903 $thisfile_video['codec'] = 'mjpeg';
904 $thisfile_video['lossless'] = false;
905 $thisfile_video['bits_per_sample'] = 24;
907 $thisfile_audio['dataformat'] = 'adpcm';
908 $thisfile_audio['lossless'] = false;
912 // http://en.wikipedia.org/wiki/CD-DA
914 $info['fileformat'] = 'cda';
915 unset($info['mime_type']);
917 $thisfile_audio_dataformat = 'cda';
919 $info['avdataoffset'] = 44;
921 if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
923 $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
925 $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
926 $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
927 $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
928 $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
929 $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
930 $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
931 $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
933 $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
934 $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
935 $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
936 $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
938 // hardcoded data for CD-audio
939 $thisfile_audio['lossless'] = true;
940 $thisfile_audio['sample_rate'] = 44100;
941 $thisfile_audio['channels'] = 2;
942 $thisfile_audio['bits_per_sample'] = 16;
943 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
944 $thisfile_audio['bitrate_mode'] = 'cbr';
948 // http://en.wikipedia.org/wiki/AIFF
951 $info['fileformat'] = 'aiff';
952 $info['mime_type'] = 'audio/x-aiff';
954 $thisfile_audio['bitrate_mode'] = 'cbr';
955 $thisfile_audio_dataformat = 'aiff';
956 $thisfile_audio['lossless'] = true;
958 if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
959 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
960 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
961 if ($info['avdataend'] > $info['filesize']) {
962 if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
963 // structures rounded to 2-byte boundary, but dumb encoders
964 // forget to pad end of file to make this actually work
966 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
968 $info['avdataend'] = $info['filesize'];
972 if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
975 $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
977 $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
978 $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
979 $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
980 $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
982 if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
983 $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
984 $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
985 $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
986 switch ($thisfile_riff_audio['codec_name']) {
988 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
989 $thisfile_audio['lossless'] = true;
993 switch ($thisfile_riff_audio['codec_fourcc']) {
994 // http://developer.apple.com/qa/snd/snd07.html
996 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
997 $thisfile_audio['lossless'] = true;
1001 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
1002 $thisfile_audio['lossless'] = true;
1011 $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
1012 $thisfile_audio['lossless'] = false;
1017 $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
1018 if ($thisfile_riff_audio['bits_per_sample'] > 0) {
1019 $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
1021 $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
1022 if ($thisfile_audio['sample_rate'] == 0) {
1023 $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
1026 $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
1029 if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
1031 $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1033 for ($i = 0; $i < $CommentCount; $i++) {
1034 $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
1036 $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1038 $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1040 $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1041 $offset += $CommentLength;
1043 $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1044 $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1048 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1049 foreach ($CommentsChunkNames as $key => $value) {
1050 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1051 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1055 if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1056 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1057 $getid3_temp = new getID3();
1058 $getid3_temp->openfile($this->getid3->filename);
1059 $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1060 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1061 if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1062 $info['id3v2'] = $getid3_temp->info['id3v2'];
1064 unset($getid3_temp, $getid3_id3v2);
1069 // http://en.wikipedia.org/wiki/8SVX
1071 $info['fileformat'] = '8svx';
1072 $info['mime_type'] = 'audio/8svx';
1074 $thisfile_audio['bitrate_mode'] = 'cbr';
1075 $thisfile_audio_dataformat = '8svx';
1076 $thisfile_audio['bits_per_sample'] = 8;
1077 $thisfile_audio['channels'] = 1; // overridden below, if need be
1079 if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1080 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1081 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1082 if ($info['avdataend'] > $info['filesize']) {
1083 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1087 if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1089 $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1091 $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
1092 $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
1093 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
1094 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1095 $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1096 $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1097 $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1099 $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1101 switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1103 $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
1104 $thisfile_audio['lossless'] = true;
1105 $ActualBitsPerSample = 8;
1109 $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
1110 $thisfile_audio['lossless'] = false;
1111 $ActualBitsPerSample = 4;
1115 $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1120 if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1121 $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1122 switch ($ChannelsIndex) {
1124 $thisfile_audio['channels'] = 2;
1127 case 2: // Left channel only
1128 case 4: // Right channel only
1129 $thisfile_audio['channels'] = 1;
1133 $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1139 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1140 foreach ($CommentsChunkNames as $key => $value) {
1141 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1142 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1146 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1147 if (!empty($thisfile_audio['bitrate'])) {
1148 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1153 $info['fileformat'] = 'vcd'; // Asume Video CD
1154 $info['mime_type'] = 'video/mpeg';
1156 if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1157 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
1159 $getid3_temp = new getID3();
1160 $getid3_temp->openfile($this->getid3->filename);
1161 $getid3_mpeg = new getid3_mpeg($getid3_temp);
1162 $getid3_mpeg->Analyze();
1163 if (empty($getid3_temp->info['error'])) {
1164 $info['audio'] = $getid3_temp->info['audio'];
1165 $info['video'] = $getid3_temp->info['video'];
1166 $info['mpeg'] = $getid3_temp->info['mpeg'];
1167 $info['warning'] = $getid3_temp->info['warning'];
1169 unset($getid3_temp, $getid3_mpeg);
1175 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1176 //unset($info['fileformat']);
1179 switch ($RIFFsubtype) {
1183 $ID3v2_key_good = 'id3 ';
1184 $ID3v2_keys_bad = array('ID3 ', 'tag ');
1185 foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1186 if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1187 $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1188 $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1192 if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1193 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1195 $getid3_temp = new getID3();
1196 $getid3_temp->openfile($this->getid3->filename);
1197 $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1198 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1199 if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1200 $info['id3v2'] = $getid3_temp->info['id3v2'];
1202 unset($getid3_temp, $getid3_id3v2);
1207 if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1208 $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1210 if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1211 self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1213 if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1214 self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1217 if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1218 $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1221 if (!isset($info['playtime_seconds'])) {
1222 $info['playtime_seconds'] = 0;
1224 if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1225 // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1226 $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1227 } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1228 $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1231 if ($info['playtime_seconds'] > 0) {
1232 if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1234 if (!isset($info['bitrate'])) {
1235 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1238 } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1240 if (!isset($thisfile_audio['bitrate'])) {
1241 $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1244 } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1246 if (!isset($thisfile_video['bitrate'])) {
1247 $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1254 if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1256 $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1257 $thisfile_audio['bitrate'] = 0;
1258 $thisfile_video['bitrate'] = $info['bitrate'];
1259 foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1260 $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1261 $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1263 if ($thisfile_video['bitrate'] <= 0) {
1264 unset($thisfile_video['bitrate']);
1266 if ($thisfile_audio['bitrate'] <= 0) {
1267 unset($thisfile_audio['bitrate']);
1271 if (isset($info['mpeg']['audio'])) {
1272 $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer'];
1273 $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1274 $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
1275 $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
1276 $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1277 if (!empty($info['mpeg']['audio']['codec'])) {
1278 $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1280 if (!empty($thisfile_audio['streams'])) {
1281 foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1282 if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1283 $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
1284 $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
1285 $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
1286 $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1287 $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
1291 $getid3_mp3 = new getid3_mp3($this->getid3);
1292 $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1297 if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1298 switch ($thisfile_audio_dataformat) {
1300 // ignore bits_per_sample
1304 $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1310 if (empty($thisfile_riff_raw)) {
1311 unset($thisfile_riff['raw']);
1313 if (empty($thisfile_riff_audio)) {
1314 unset($thisfile_riff['audio']);
1316 if (empty($thisfile_riff_video)) {
1317 unset($thisfile_riff['video']);
1323 public function ParseRIFFAMV($startoffset, $maxoffset) {
1324 // 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
1326 // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
1327 //typedef struct _amvmainheader {
1328 //FOURCC fcc; // 'amvh'
1330 //DWORD dwMicroSecPerFrame;
1342 $info = &$this->getid3->info;
1347 $this->fseek($startoffset);
1348 $maxoffset = min($maxoffset, $info['avdataend']);
1349 $AMVheader = $this->fread(284);
1350 if (substr($AMVheader, 0, 8) != 'hdrlamvh') {
1351 throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"');
1353 if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
1354 throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"');
1356 $RIFFchunk = array();
1357 $RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4));
1358 $RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved?
1359 $RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4));
1360 $RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4));
1361 $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4));
1362 $RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved?
1363 $RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved?
1364 $RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1));
1365 $RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1));
1366 $RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2));
1368 $info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
1369 $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
1370 $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
1371 $info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
1373 // 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
1375 if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
1376 throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"');
1378 // followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144
1379 if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") {
1380 throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"');
1382 // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
1384 if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
1385 throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
1387 // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
1388 if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") {
1389 throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"');
1391 // followed by 20 bytes of a modified WAVEFORMATEX:
1393 // WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code)
1394 // WORD nChannels; //(Fixme: this is always 1)
1395 // DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050)
1396 // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
1397 // WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?)
1398 // WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
1399 // WORD cbSize; //(Fixme: this seems to be 0 in AMV files)
1402 $RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2));
1403 $RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2));
1404 $RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4));
1405 $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4));
1406 $RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2));
1407 $RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2));
1408 $RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2));
1409 $RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2));
1412 $info['audio']['lossless'] = false;
1413 $info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec'];
1414 $info['audio']['channels'] = $RIFFchunk['strf']['nchannels'];
1415 $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
1416 $info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
1417 $info['audio']['bitrate_mode'] = 'cbr';
1420 } catch (getid3_exception $e) {
1421 if ($e->getCode() == 10) {
1422 $this->warning('RIFFAMV parser: '.$e->getMessage());
1432 public function ParseRIFF($startoffset, $maxoffset) {
1433 $info = &$this->getid3->info;
1436 $FoundAllChunksWeNeed = false;
1439 $this->fseek($startoffset);
1440 $maxoffset = min($maxoffset, $info['avdataend']);
1441 while ($this->ftell() < $maxoffset) {
1442 $chunknamesize = $this->fread(8);
1443 //$chunkname = substr($chunknamesize, 0, 4);
1444 $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
1445 $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1446 //if (strlen(trim($chunkname, "\x00")) < 4) {
1447 if (strlen($chunkname) < 4) {
1448 $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1451 if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1452 $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1455 if (($chunksize % 2) != 0) {
1456 // all structures are packed on word boundaries
1460 switch ($chunkname) {
1462 $listname = $this->fread(4);
1463 if (preg_match('#^(movi|rec )$#i', $listname)) {
1464 $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1465 $RIFFchunk[$listname]['size'] = $chunksize;
1467 if (!$FoundAllChunksWeNeed) {
1468 $WhereWeWere = $this->ftell();
1469 $AudioChunkHeader = $this->fread(12);
1470 $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
1471 $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
1472 $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1474 if ($AudioChunkStreamType == 'wb') {
1475 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1476 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1478 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1479 $getid3_temp = new getID3();
1480 $getid3_temp->openfile($this->getid3->filename);
1481 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1482 $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1483 $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1484 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1485 if (isset($getid3_temp->info['mpeg']['audio'])) {
1486 $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
1487 $info['audio'] = $getid3_temp->info['audio'];
1488 $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
1489 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1490 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1491 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1492 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1493 //$info['bitrate'] = $info['audio']['bitrate'];
1495 unset($getid3_temp, $getid3_mp3);
1498 } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1501 $getid3_temp = new getID3();
1502 $getid3_temp->openfile($this->getid3->filename);
1503 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1504 $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
1505 $getid3_ac3 = new getid3_ac3($getid3_temp);
1506 $getid3_ac3->Analyze();
1507 if (empty($getid3_temp->info['error'])) {
1508 $info['audio'] = $getid3_temp->info['audio'];
1509 $info['ac3'] = $getid3_temp->info['ac3'];
1510 if (!empty($getid3_temp->info['warning'])) {
1511 foreach ($getid3_temp->info['warning'] as $key => $value) {
1512 $info['warning'][] = $value;
1516 unset($getid3_temp, $getid3_ac3);
1519 $FoundAllChunksWeNeed = true;
1520 $this->fseek($WhereWeWere);
1522 $this->fseek($chunksize - 4, SEEK_CUR);
1526 if (!isset($RIFFchunk[$listname])) {
1527 $RIFFchunk[$listname] = array();
1529 $LISTchunkParent = $listname;
1530 $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1531 if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1532 $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1539 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1540 $this->fseek($chunksize, SEEK_CUR);
1544 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1545 $thisindex = count($RIFFchunk[$chunkname]);
1547 $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1548 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
1549 switch ($chunkname) {
1551 $info['avdataoffset'] = $this->ftell();
1552 $info['avdataend'] = $info['avdataoffset'] + $chunksize;
1554 $testData = $this->fread(36);
1555 if ($testData === '') {
1558 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1560 // Probably is MP3 data
1561 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1562 $getid3_temp = new getID3();
1563 $getid3_temp->openfile($this->getid3->filename);
1564 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1565 $getid3_temp->info['avdataend'] = $info['avdataend'];
1566 $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1567 $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1568 if (empty($getid3_temp->info['error'])) {
1569 $info['audio'] = $getid3_temp->info['audio'];
1570 $info['mpeg'] = $getid3_temp->info['mpeg'];
1572 unset($getid3_temp, $getid3_mp3);
1575 } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1577 // This is probably AC-3 data
1578 $getid3_temp = new getID3();
1579 if ($isRegularAC3) {
1580 $getid3_temp->openfile($this->getid3->filename);
1581 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1582 $getid3_temp->info['avdataend'] = $info['avdataend'];
1584 $getid3_ac3 = new getid3_ac3($getid3_temp);
1585 if ($isRegularAC3) {
1586 $getid3_ac3->Analyze();
1588 // Dolby Digital WAV
1589 // AC-3 content, but not encoded in same format as normal AC-3 file
1590 // For one thing, byte order is swapped
1592 for ($i = 0; $i < 28; $i += 2) {
1593 $ac3_data .= substr($testData, 8 + $i + 1, 1);
1594 $ac3_data .= substr($testData, 8 + $i + 0, 1);
1596 $getid3_ac3->AnalyzeString($ac3_data);
1599 if (empty($getid3_temp->info['error'])) {
1600 $info['audio'] = $getid3_temp->info['audio'];
1601 $info['ac3'] = $getid3_temp->info['ac3'];
1602 if (!empty($getid3_temp->info['warning'])) {
1603 foreach ($getid3_temp->info['warning'] as $newerror) {
1604 $this->warning('getid3_ac3() says: ['.$newerror.']');
1608 unset($getid3_temp, $getid3_ac3);
1610 } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1612 // This is probably DTS data
1613 $getid3_temp = new getID3();
1614 $getid3_temp->openfile($this->getid3->filename);
1615 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1616 $getid3_dts = new getid3_dts($getid3_temp);
1617 $getid3_dts->Analyze();
1618 if (empty($getid3_temp->info['error'])) {
1619 $info['audio'] = $getid3_temp->info['audio'];
1620 $info['dts'] = $getid3_temp->info['dts'];
1621 $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1622 if (!empty($getid3_temp->info['warning'])) {
1623 foreach ($getid3_temp->info['warning'] as $newerror) {
1624 $this->warning('getid3_dts() says: ['.$newerror.']');
1629 unset($getid3_temp, $getid3_dts);
1631 } elseif (substr($testData, 0, 4) == 'wvpk') {
1633 // This is WavPack data
1634 $info['wavpack']['offset'] = $info['avdataoffset'];
1635 $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1636 $this->parseWavPackHeader(substr($testData, 8, 28));
1639 // This is some other kind of data (quite possibly just PCM)
1640 // do nothing special, just skip it
1642 $nextoffset = $info['avdataend'];
1643 $this->fseek($nextoffset);
1655 // always read data in
1657 // should be: never read data in
1658 // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1659 if ($chunksize < 1048576) {
1660 if ($chunksize > 0) {
1661 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1662 if ($chunkname == 'JUNK') {
1663 if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1664 // only keep text characters [chr(32)-chr(127)]
1665 $info['riff']['comments']['junk'][] = trim($matches[1]);
1667 // but if nothing there, ignore
1668 // remove the key in either case
1669 unset($RIFFchunk[$chunkname][$thisindex]['data']);
1673 $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1674 $this->fseek($chunksize, SEEK_CUR);
1679 // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1683 if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1684 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1685 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
1686 unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1687 unset($RIFFchunk[$chunkname][$thisindex]['size']);
1688 if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1689 unset($RIFFchunk[$chunkname][$thisindex]);
1691 if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1692 unset($RIFFchunk[$chunkname]);
1694 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1695 } elseif ($chunksize < 2048) {
1696 // only read data in if smaller than 2kB
1697 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1699 $this->fseek($chunksize, SEEK_CUR);
1707 } catch (getid3_exception $e) {
1708 if ($e->getCode() == 10) {
1709 $this->warning('RIFF parser: '.$e->getMessage());
1718 public function ParseRIFFdata(&$RIFFdata) {
1719 $info = &$this->getid3->info;
1721 $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1722 $fp_temp = fopen($tempfile, 'wb');
1723 $RIFFdataLength = strlen($RIFFdata);
1724 $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1725 for ($i = 0; $i < 4; $i++) {
1726 $RIFFdata[($i + 4)] = $NewLengthString[$i];
1728 fwrite($fp_temp, $RIFFdata);
1731 $getid3_temp = new getID3();
1732 $getid3_temp->openfile($tempfile);
1733 $getid3_temp->info['filesize'] = $RIFFdataLength;
1734 $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1735 $getid3_temp->info['tags'] = $info['tags'];
1736 $getid3_temp->info['warning'] = $info['warning'];
1737 $getid3_temp->info['error'] = $info['error'];
1738 $getid3_temp->info['comments'] = $info['comments'];
1739 $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array());
1740 $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array());
1741 $getid3_riff = new getid3_riff($getid3_temp);
1742 $getid3_riff->Analyze();
1744 $info['riff'] = $getid3_temp->info['riff'];
1745 $info['warning'] = $getid3_temp->info['warning'];
1746 $info['error'] = $getid3_temp->info['error'];
1747 $info['tags'] = $getid3_temp->info['tags'];
1748 $info['comments'] = $getid3_temp->info['comments'];
1749 unset($getid3_riff, $getid3_temp);
1755 public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1756 $RIFFinfoKeyLookup = array(
1757 'IARL'=>'archivallocation',
1759 'ICDS'=>'costumedesigner',
1760 'ICMS'=>'commissionedby',
1763 'ICOP'=>'copyright',
1764 'ICRD'=>'creationdate',
1765 'IDIM'=>'dimensions',
1766 'IDIT'=>'digitizationdate',
1767 'IDPI'=>'resolution',
1768 'IDST'=>'distributor',
1770 'IENG'=>'engineers',
1771 'IFRM'=>'accountofparts',
1774 'ILGT'=>'lightness',
1776 'IMED'=>'orignalmedium',
1779 'IPDS'=>'productiondesigner',
1787 'ISGN'=>'secondarygenre',
1788 'ISHP'=>'sharpness',
1789 'ISRC'=>'sourcesupplier',
1790 'ISRF'=>'digitizationsource',
1791 'ISTD'=>'productionstudio',
1793 'ITCH'=>'encoded_by',
1798 foreach ($RIFFinfoKeyLookup as $key => $value) {
1799 if (isset($RIFFinfoArray[$key])) {
1800 foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1801 if (trim($commentdata['data']) != '') {
1802 if (isset($CommentsTargetArray[$value])) {
1803 $CommentsTargetArray[$value][] = trim($commentdata['data']);
1805 $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1814 public static function parseWAVEFORMATex($WaveFormatExData) {
1816 $WaveFormatEx['raw'] = array();
1817 $WaveFormatEx_raw = &$WaveFormatEx['raw'];
1819 $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2);
1820 $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2);
1821 $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4);
1822 $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4);
1823 $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2);
1824 $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2);
1825 if (strlen($WaveFormatExData) > 16) {
1826 $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2);
1828 $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1830 $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1831 $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels'];
1832 $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec'];
1833 $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1834 $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1836 return $WaveFormatEx;
1839 public function parseWavPackHeader($WavPackChunkData) {
1844 // short bits; // added for version 2.00
1845 // short flags, shift; // added for version 3.00
1846 // long total_samples, crc, crc2;
1847 // char extension [4], extra_bc, extras [3];
1851 $info = &$this->getid3->info;
1852 $info['wavpack'] = array();
1853 $thisfile_wavpack = &$info['wavpack'];
1855 $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2));
1856 if ($thisfile_wavpack['version'] >= 2) {
1857 $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2));
1859 if ($thisfile_wavpack['version'] >= 3) {
1860 $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2));
1861 $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2));
1862 $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4));
1863 $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1864 $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1865 $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4);
1866 $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1867 for ($i = 0; $i <= 2; $i++) {
1868 $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1872 $thisfile_wavpack['flags'] = array();
1873 $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1875 $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1876 $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1877 $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1878 $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1879 $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1880 $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1881 $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1882 $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1883 $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1884 $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1885 $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1886 $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1887 $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1888 $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1889 $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1890 $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1891 $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1892 $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1893 $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1894 $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1900 public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1902 $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1903 $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels
1904 $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
1905 $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1906 $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1907 $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1908 $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1909 $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1910 $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
1911 $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
1912 $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1914 $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier
1919 public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1920 // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1921 // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1922 // 'Byte Layout: '1111111111111111
1923 // '32 for Movie - 1 '1111111111111111
1924 // '28 for Author - 6 '6666666666666666
1925 // '4 for year - 2 '6666666666662222
1926 // '3 for genre - 3 '7777777777777777
1927 // '48 for Comments - 7 '7777777777777777
1928 // '1 for Rating - 4 '7777777777777777
1929 // '5 for Future Additions - 0 '333400000DIVXTAG
1932 static $DIVXTAGgenre = array(
1934 1 => 'Action/Adventure',
1946 13 => 'Infomercial',
1947 14 => 'Interactive',
1949 16 => 'Music Video',
1956 $DIVXTAGrating = array(
1965 $parsed['title'] = trim(substr($DIVXTAG, 0, 32));
1966 $parsed['artist'] = trim(substr($DIVXTAG, 32, 28));
1967 $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4)));
1968 $parsed['comment'] = trim(substr($DIVXTAG, 64, 48));
1969 $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3)));
1970 $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1));
1971 //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null
1972 //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG"
1974 $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']);
1975 $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1978 unset($parsed['genre_id'], $parsed['rating_id']);
1979 foreach ($parsed as $key => $value) {
1980 if (!$value === '') {
1981 unset($parsed['key']);
1986 foreach ($parsed as $tag => $value) {
1987 $parsed[$tag] = array($value);
1993 public static function waveSNDMtagLookup($tagshortname) {
1996 /** This is not a comment!
2003 ©fin featuredinstrument
2013 return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
2016 public static function wFormatTagLookup($wFormatTag) {
2020 /** This is not a comment!
2022 0x0000 Microsoft Unknown Wave Format
2023 0x0001 Pulse Code Modulation (PCM)
2024 0x0002 Microsoft ADPCM
2026 0x0004 Compaq Computer VSELP
2028 0x0006 Microsoft A-Law
2029 0x0007 Microsoft mu-Law
2030 0x0008 Microsoft DTS
2032 0x0011 Intel DVI/IMA ADPCM
2033 0x0012 Videologic MediaSpace ADPCM
2034 0x0013 Sierra Semiconductor ADPCM
2035 0x0014 Antex Electronics G.723 ADPCM
2036 0x0015 DSP Solutions DigiSTD
2037 0x0016 DSP Solutions DigiFIX
2038 0x0017 Dialogic OKI ADPCM
2039 0x0018 MediaVision ADPCM
2040 0x0019 Hewlett-Packard CU
2042 0x0021 Speech Compression Sonarc
2043 0x0022 DSP Group TrueSpeech
2044 0x0023 Echo Speech EchoSC1
2045 0x0024 Audiofile AF36
2046 0x0025 Audio Processing Technology APTX
2047 0x0026 AudioFile AF10
2051 0x0031 Microsoft GSM 6.10
2053 0x0033 Antex Electronics ADPCME
2054 0x0034 Control Resources VQLPC
2055 0x0035 DSP Solutions DigiREAL
2056 0x0036 DSP Solutions DigiADPCM
2057 0x0037 Control Resources CR10
2058 0x0038 Natural MicroSystems VBXADPCM
2059 0x0039 Crystal Semiconductor IMA ADPCM
2061 0x003B Rockwell ADPCM
2062 0x003C Rockwell Digit LK
2064 0x0040 Antex Electronics G.721 ADPCM
2067 0x0050 MPEG Layer-2 or Layer-1
2075 0x0063 Canopus Atrac
2080 0x0069 Voxware Byte Aligned
2085 0x0074 Voxware MetaVoice
2086 0x0075 Voxware MetaSound
2087 0x0076 Voxware RT29HW
2101 0x0092 Dolby AC3 SPDIF
2102 0x0093 MediaSonic G.723
2103 0x0094 Aculab PLC Prosody 8kbps
2105 0x0098 Philips LPCBB
2108 0x0100 Rhetorex ADPCM
2111 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
2114 0x0123 Digital G.723
2115 0x0125 Sanyo LD ADPCM
2116 0x0130 Sipro Lab Telecom ACELP NET
2117 0x0131 Sipro Lab Telecom ACELP 4800
2118 0x0132 Sipro Lab Telecom ACELP 8V3
2119 0x0133 Sipro Lab Telecom G.729
2120 0x0134 Sipro Lab Telecom G.729A
2121 0x0135 Sipro Lab Telecom Kelvin
2122 0x0140 Windows Media Video V8