wp/wp-includes/ID3/module.audio-video.riff.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 16 a86126ab1dd4
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
   188 				if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
   188 				if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
   189 
   189 
   190 					$thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($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'];
   191 					$thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
   192 					if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
   192 					if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
   193 						$info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
   193 						$this->error('Corrupt RIFF file: bitrate_audio == zero');
   194 						return false;
   194 						return false;
   195 					}
   195 					}
   196 					$thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
   196 					$thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
   197 					unset($thisfile_riff_audio[$streamindex]['raw']);
   197 					unset($thisfile_riff_audio[$streamindex]['raw']);
   198 					$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
   198 					$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
   199 
   199 
   200 					$thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $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') {
   201 					if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
   202 						$info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
   202 						$this->warning('Audio codec = '.$thisfile_audio['codec']);
   203 					}
   203 					}
   204 					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
   204 					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
   205 
   205 
   206 					if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
   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']);
   207 						$info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
   300 						if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
   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;
   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;
   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']);
   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 {
   304 						} else {
   305 							$info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
   305 							$this->warning('RIFF.WAVE.BEXT.origin_time is invalid');
   306 						}
   306 						}
   307 					} else {
   307 					} else {
   308 						$info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
   308 						$this->warning('RIFF.WAVE.BEXT.origin_date is invalid');
   309 					}
   309 					}
   310 					$thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
   310 					$thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
   311 					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
   311 					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
   312 				}
   312 				}
   313 
   313 
   383 						$SNDM_thisTagOffset += 2;
   383 						$SNDM_thisTagOffset += 2;
   384 						$SNDM_thisTagDataText =                            substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
   384 						$SNDM_thisTagDataText =                            substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
   385 						$SNDM_thisTagOffset += $SNDM_thisTagDataSize;
   385 						$SNDM_thisTagOffset += $SNDM_thisTagDataSize;
   386 
   386 
   387 						if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $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).')';
   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;
   389 							break;
   390 						} elseif ($SNDM_thisTagSize <= 0) {
   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).')';
   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;
   392 							break;
   393 						}
   393 						}
   394 						$SNDM_startoffset += $SNDM_thisTagSize;
   394 						$SNDM_startoffset += $SNDM_thisTagSize;
   395 
   395 
   396 						$thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
   396 						$thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
   397 						if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
   397 						if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
   398 							$thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
   398 							$thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
   399 						} else {
   399 						} else {
   400 							$info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
   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 						}
   401 						}
   402 					}
   402 					}
   403 
   403 
   404 					$tagmapping = array(
   404 					$tagmapping = array(
   405 						'tracktitle'=>'title',
   405 						'tracktitle'=>'title',
   426 							@list($numerator, $denominator) = explode('/', $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);
   427 							$thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
   428 						}
   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'])) {
   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'));
   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'];
   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;
   432 							$h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
   433 							$h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
   433 							$m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600))      / 60);
   434 							$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 							$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 							$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']       = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s,       $f);
   437 							$thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d',   $h, $m, $s, round($f));
   438 							$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);
   438 						}
   440 						}
   439 						unset($parsedXML);
   441 						unset($parsedXML);
   440 					}
   442 					}
   441 				}
   443 				}
   442 
   444 
   568 								// LiteWave appears to incorrectly *not* pad actual output file
   570 								// LiteWave appears to incorrectly *not* pad actual output file
   569 								// to nearest WORD boundary so may appear to be short by one
   571 								// to nearest WORD boundary so may appear to be short by one
   570 								// byte, in which case - skip warning
   572 								// byte, in which case - skip warning
   571 							} else {
   573 							} else {
   572 								// Short by more than one byte, throw warning
   574 								// 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)';
   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)');
   574 								$info['avdataend'] = $info['filesize'];
   576 								$info['avdataend'] = $info['filesize'];
   575 							}
   577 							}
   576 							break;
   578 							break;
   577 
   579 
   578 						default:
   580 						default:
   579 							if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
   581 							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
   582 								// output file appears to be incorrectly *not* padded to nearest WORD boundary
   581 								// Output less severe warning
   583 								// 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)';
   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)');
   583 								$info['avdataend'] = $info['filesize'];
   585 								$info['avdataend'] = $info['filesize'];
   584 							} else {
   586 							} else {
   585 								// Short by more than one byte, throw warning
   587 								// 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)';
   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)');
   587 								$info['avdataend'] = $info['filesize'];
   589 								$info['avdataend'] = $info['filesize'];
   588 							}
   590 							}
   589 							break;
   591 							break;
   590 					}
   592 					}
   591 				}
   593 				}
   592 				if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
   594 				if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
   593 					if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
   595 					if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
   594 						$info['avdataend']--;
   596 						$info['avdataend']--;
   595 						$info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
   597 						$this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored');
   596 					}
   598 					}
   597 				}
   599 				}
   598 				if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
   600 				if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
   599 					unset($thisfile_audio['bits_per_sample']);
   601 					unset($thisfile_audio['bits_per_sample']);
   600 					if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
   602 					if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
   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['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
   618 					} else {
   620 					} else {
   619 						$info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
   621 						$info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
   620 					}
   622 					}
   621 					if ($info['avdataend'] > $info['filesize']) {
   623 					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)';
   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)');
   623 						$info['avdataend'] = $info['filesize'];
   625 						$info['avdataend'] = $info['filesize'];
   624 					}
   626 					}
   625 				}
   627 				}
   626 
   628 
   627 				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
   629 				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
   658 					$thisfile_riff_raw['avih'] = array();
   660 					$thisfile_riff_raw['avih'] = array();
   659 					$thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
   661 					$thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
   660 
   662 
   661 					$thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
   663 					$thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
   662 					if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
   664 					if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
   663 						$info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
   665 						$this->error('Corrupt RIFF file: avih.dwMicroSecPerFrame == zero');
   664 						return false;
   666 						return false;
   665 					}
   667 					}
   666 
   668 
   667 					$flags = array(
   669 					$flags = array(
   668 						'dwMaxBytesPerSec',       // max. transfer rate
   670 						'dwMaxBytesPerSec',       // max. transfer rate
   856 													break;
   858 													break;
   857 											}
   859 											}
   858 											break;
   860 											break;
   859 
   861 
   860 										default:
   862 										default:
   861 											$info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
   863 											$this->warning('Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"');
   862 											break;
   864 											break;
   863 
   865 
   864 									}
   866 									}
   865 								}
   867 								}
   866 							}
   868 							}
   961 					if ($info['avdataend'] > $info['filesize']) {
   963 					if ($info['avdataend'] > $info['filesize']) {
   962 						if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
   964 						if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
   963 							// structures rounded to 2-byte boundary, but dumb encoders
   965 							// structures rounded to 2-byte boundary, but dumb encoders
   964 							// forget to pad end of file to make this actually work
   966 							// forget to pad end of file to make this actually work
   965 						} else {
   967 						} else {
   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 							$this->warning('Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found');
   967 						}
   969 						}
   968 						$info['avdataend'] = $info['filesize'];
   970 						$info['avdataend'] = $info['filesize'];
   969 					}
   971 					}
   970 				}
   972 				}
   971 
   973 
  1018 					if ($thisfile_riff_audio['bits_per_sample'] > 0) {
  1020 					if ($thisfile_riff_audio['bits_per_sample'] > 0) {
  1019 						$thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
  1021 						$thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
  1020 					}
  1022 					}
  1021 					$thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
  1023 					$thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
  1022 					if ($thisfile_audio['sample_rate'] == 0) {
  1024 					if ($thisfile_audio['sample_rate'] == 0) {
  1023 						$info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
  1025 						$this->error('Corrupted AIFF file: sample_rate == zero');
  1024 						return false;
  1026 						return false;
  1025 					}
  1027 					}
  1026 					$info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
  1028 					$info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
  1027 				}
  1029 				}
  1028 
  1030 
  1078 
  1080 
  1079 				if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
  1081 				if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
  1080 					$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
  1082 					$info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
  1081 					$info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
  1083 					$info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
  1082 					if ($info['avdataend'] > $info['filesize']) {
  1084 					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';
  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');
  1084 					}
  1086 					}
  1085 				}
  1087 				}
  1086 
  1088 
  1087 				if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
  1089 				if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
  1088 					// shortcut
  1090 					// shortcut
  1110 							$thisfile_audio['lossless'] = false;
  1112 							$thisfile_audio['lossless'] = false;
  1111 							$ActualBitsPerSample        = 4;
  1113 							$ActualBitsPerSample        = 4;
  1112 							break;
  1114 							break;
  1113 
  1115 
  1114 						default:
  1116 						default:
  1115 							$info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
  1117 							$this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"');
  1116 							break;
  1118 							break;
  1117 					}
  1119 					}
  1118 				}
  1120 				}
  1119 
  1121 
  1120 				if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
  1122 				if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
  1128 						case 4: // Right channel only
  1130 						case 4: // Right channel only
  1129 							$thisfile_audio['channels'] = 1;
  1131 							$thisfile_audio['channels'] = 1;
  1130 							break;
  1132 							break;
  1131 
  1133 
  1132 						default:
  1134 						default:
  1133 							$info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
  1135 							$this->warning('Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"');
  1134 							break;
  1136 							break;
  1135 					}
  1137 					}
  1136 
  1138 
  1137 				}
  1139 				}
  1138 
  1140 
  1168 					}
  1170 					}
  1169 					unset($getid3_temp, $getid3_mpeg);
  1171 					unset($getid3_temp, $getid3_mpeg);
  1170 				}
  1172 				}
  1171 				break;
  1173 				break;
  1172 
  1174 
       
  1175 			case 'WEBP':
       
  1176 				// https://developers.google.com/speed/webp/docs/riff_container
       
  1177 				// https://tools.ietf.org/html/rfc6386
       
  1178 				// https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
       
  1179 				$info['fileformat'] = 'webp';
       
  1180 				$info['mime_type']  = 'image/webp';
       
  1181 
       
  1182 				if (!empty($thisfile_riff['WEBP']['VP8 '][0]['size'])) {
       
  1183 					$old_offset = $this->ftell();
       
  1184 					$this->fseek($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8); // 4 bytes "VP8 " + 4 bytes chunk size
       
  1185 					$WEBP_VP8_header = $this->fread(10);
       
  1186 					$this->fseek($old_offset);
       
  1187 					if (substr($WEBP_VP8_header, 3, 3) == "\x9D\x01\x2A") {
       
  1188 						$thisfile_riff['WEBP']['VP8 '][0]['keyframe']   = !(getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x800000);
       
  1189 						$thisfile_riff['WEBP']['VP8 '][0]['version']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x700000) >> 20;
       
  1190 						$thisfile_riff['WEBP']['VP8 '][0]['show_frame'] =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x080000);
       
  1191 						$thisfile_riff['WEBP']['VP8 '][0]['data_bytes'] =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x07FFFF) >>  0;
       
  1192 
       
  1193 						$thisfile_riff['WEBP']['VP8 '][0]['scale_x']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0xC000) >> 14;
       
  1194 						$thisfile_riff['WEBP']['VP8 '][0]['width']      =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0x3FFF);
       
  1195 						$thisfile_riff['WEBP']['VP8 '][0]['scale_y']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0xC000) >> 14;
       
  1196 						$thisfile_riff['WEBP']['VP8 '][0]['height']     =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0x3FFF);
       
  1197 
       
  1198 						$info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8 '][0]['width'];
       
  1199 						$info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8 '][0]['height'];
       
  1200 					} else {
       
  1201 						$this->error('Expecting 9D 01 2A at offset '.($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8 + 3).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8_header, 3, 3)).'"');
       
  1202 					}
       
  1203 
       
  1204 				}
       
  1205 				if (!empty($thisfile_riff['WEBP']['VP8L'][0]['size'])) {
       
  1206 					$old_offset = $this->ftell();
       
  1207 					$this->fseek($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8); // 4 bytes "VP8L" + 4 bytes chunk size
       
  1208 					$WEBP_VP8L_header = $this->fread(10);
       
  1209 					$this->fseek($old_offset);
       
  1210 					if (substr($WEBP_VP8L_header, 0, 1) == "\x2F") {
       
  1211 						$width_height_flags = getid3_lib::LittleEndian2Bin(substr($WEBP_VP8L_header, 1, 4));
       
  1212 						$thisfile_riff['WEBP']['VP8L'][0]['width']         =        bindec(substr($width_height_flags, 18, 14)) + 1;
       
  1213 						$thisfile_riff['WEBP']['VP8L'][0]['height']        =        bindec(substr($width_height_flags,  4, 14)) + 1;
       
  1214 						$thisfile_riff['WEBP']['VP8L'][0]['alpha_is_used'] = (bool) bindec(substr($width_height_flags,  3,  1));
       
  1215 						$thisfile_riff['WEBP']['VP8L'][0]['version']       =        bindec(substr($width_height_flags,  0,  3));
       
  1216 
       
  1217 						$info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8L'][0]['width'];
       
  1218 						$info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8L'][0]['height'];
       
  1219 					} else {
       
  1220 						$this->error('Expecting 2F at offset '.($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8L_header, 0, 1)).'"');
       
  1221 					}
       
  1222 
       
  1223 				}
       
  1224 				break;
  1173 
  1225 
  1174 			default:
  1226 			default:
  1175 				$info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
  1227 				$this->error('Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA|WEBP), found "'.$RIFFsubtype.'" instead');
  1176 				//unset($info['fileformat']);
  1228 				//unset($info['fileformat']);
  1177 		}
  1229 		}
  1178 
  1230 
  1179 		switch ($RIFFsubtype) {
  1231 		switch ($RIFFsubtype) {
  1180 			case 'WAVE':
  1232 			case 'WAVE':
  1183 				$ID3v2_key_good = 'id3 ';
  1235 				$ID3v2_key_good = 'id3 ';
  1184 				$ID3v2_keys_bad = array('ID3 ', 'tag ');
  1236 				$ID3v2_keys_bad = array('ID3 ', 'tag ');
  1185 				foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
  1237 				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])) {
  1238 					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];
  1239 						$thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
  1188 						$info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
  1240 						$this->warning('mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"');
  1189 					}
  1241 					}
  1190 				}
  1242 				}
  1191 
  1243 
  1192 				if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
  1244 				if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
  1193 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
  1245 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
  1507 										if (empty($getid3_temp->info['error'])) {
  1559 										if (empty($getid3_temp->info['error'])) {
  1508 											$info['audio']   = $getid3_temp->info['audio'];
  1560 											$info['audio']   = $getid3_temp->info['audio'];
  1509 											$info['ac3']     = $getid3_temp->info['ac3'];
  1561 											$info['ac3']     = $getid3_temp->info['ac3'];
  1510 											if (!empty($getid3_temp->info['warning'])) {
  1562 											if (!empty($getid3_temp->info['warning'])) {
  1511 												foreach ($getid3_temp->info['warning'] as $key => $value) {
  1563 												foreach ($getid3_temp->info['warning'] as $key => $value) {
  1512 													$info['warning'][] = $value;
  1564 													$this->warning($value);
  1513 												}
  1565 												}
  1514 											}
  1566 											}
  1515 										}
  1567 										}
  1516 										unset($getid3_temp, $getid3_ac3);
  1568 										unset($getid3_temp, $getid3_ac3);
  1517 									}
  1569 									}