Пример #1
0
 public function saveAttachment(&$ThisFileInfoIndex, $filename, $offset, $length)
 {
     try {
         if (!getid3_lib::intValueSupported($offset + $length)) {
             throw new Exception('cannot extract attachment, it extends beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit');
         }
         // do not extract at all
         if ($this->option_save_attachments === getID3::ATTACHMENTS_NONE) {
             unset($ThisFileInfoIndex);
             // do not set any
             // extract to return array
         } elseif ($this->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
             // get whole data in one pass, till it is anyway stored in memory
             $ThisFileInfoIndex = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
             if ($ThisFileInfoIndex === false || strlen($ThisFileInfoIndex) != $length) {
                 // verify
                 throw new Exception('failed to read attachment data');
             }
             // assume directory path is given
         } else {
             $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->option_save_attachments), DIRECTORY_SEPARATOR);
             // check supplied directory
             if (!is_dir($dir) || !is_writable($dir)) {
                 throw new Exception('getID3::saveAttachment() -- supplied path (' . $dir . ') does not exist, or is not writable');
             }
             // set up destination path
             $dest = $dir . DIRECTORY_SEPARATOR . $filename;
             // optimize speed if read buffer size is configured to be large enough
             // here stream_copy_to_stream() may also be used. need to do speed-compare tests
             if ($length <= $this->fread_buffer_size()) {
                 $data = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
                 if ($data === false || strlen($data) != $length) {
                     // verify
                     throw new Exception('failed to read attachment data');
                 }
                 if (!file_put_contents($dest, $data)) {
                     throw new Exception('failed to create file ' . $dest);
                 }
             } else {
                 // optimization not available - copy data in loop
                 // here stream_copy_to_stream() shouldn't be used because it's internal read buffer may be larger than ours!
                 getid3_lib::CopyFileParts($this->info['filenamepath'], $dest, $offset, $length);
             }
             $ThisFileInfoIndex = $dest;
         }
     } catch (Exception $e) {
         unset($ThisFileInfoIndex);
         // do not set any is case of error
         $this->warning('Failed to extract attachment ' . $filename . ': ' . $e->getMessage());
         return false;
     }
     return true;
 }
Пример #2
0
 static function CopyFileParts($filename_source, $filename_dest, $offset, $length)
 {
     if (!getid3_lib::intValueSupported($offset + $length)) {
         throw new Exception('cannot copy file portion, it extends beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit');
     }
     if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
         if (is_writable($filename_dest) && is_file($filename_dest) && ($fp_dest = fopen($filename_dest, 'wb'))) {
             if (fseek($fp_src, $offset, SEEK_SET) == 0) {
                 $byteslefttowrite = $length;
                 while ($byteslefttowrite > 0 && ($buffer = fread($fp_src, min($byteslefttowrite, $this->getid3->fread_buffer_size())))) {
                     $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
                     $byteslefttowrite -= $byteswritten;
                 }
                 return true;
             } else {
                 throw new Exception('failed to seek to offset ' . $offset . ' in ' . $this->info['filenamepath']);
             }
             fclose($fp_dest);
         } else {
             throw new Exception('failed to open file for reading ' . $this->info['filenamepath']);
         }
         fclose($fp_src);
     } else {
         throw new Exception('failed to create file for writing ' . $dest);
     }
     return false;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'quicktime';
     $info['quicktime']['hinting'] = false;
     $info['quicktime']['controller'] = 'standard';
     // may be overridden if 'ctyp' atom is present
     $this->fseek($info['avdataoffset']);
     $offset = 0;
     $atomcounter = 0;
     while ($offset < $info['avdataend']) {
         if (!getid3_lib::intValueSupported($offset)) {
             $info['error'][] = 'Unable to parse atom at offset ' . $offset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
             break;
         }
         $this->fseek($offset);
         $AtomHeader = $this->fread(8);
         $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
         $atomname = substr($AtomHeader, 4, 4);
         // 64-bit MOV patch by jlegateØktnc*com
         if ($atomsize == 1) {
             $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
         }
         $info['quicktime'][$atomname]['name'] = $atomname;
         $info['quicktime'][$atomname]['size'] = $atomsize;
         $info['quicktime'][$atomname]['offset'] = $offset;
         if ($offset + $atomsize > $info['avdataend']) {
             $info['error'][] = 'Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atomsize . ' bytes)';
             return false;
         }
         if ($atomsize == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         $atomHierarchy = array();
         $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread($atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
         $offset += $atomsize;
         $atomcounter++;
     }
     if (!empty($info['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $info['avdataend'] = $info['avdataend_tmp'];
         unset($info['avdataend_tmp']);
     }
     if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
         $info['audio']['bitrate'] = $info['bitrate'];
     }
     if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
         foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
             $samples_per_second = $samples_count / $info['playtime_seconds'];
             if ($samples_per_second > 240) {
                 // has to be audio samples
             } else {
                 $info['video']['frame_rate'] = $samples_per_second;
                 break;
             }
         }
     }
     if ($info['audio']['dataformat'] == 'mp4' && empty($info['video']['resolution_x'])) {
         $info['fileformat'] = 'mp4';
         $info['mime_type'] = 'audio/mp4';
         unset($info['video']['dataformat']);
     }
     if (!$this->ReturnAtomData) {
         unset($info['quicktime']['moov']);
     }
     if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
         $info['audio']['dataformat'] = 'quicktime';
     }
     if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
         $info['video']['dataformat'] = 'quicktime';
     }
     return true;
 }
Пример #4
0
 function RemoveID3v1()
 {
     // File MUST be writeable - CHMOD(646) at least
     if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
         $this->setRealFileSize();
         if ($this->filesize <= 0 || !getid3_lib::intValueSupported($this->filesize)) {
             $this->errors[] = 'Unable to RemoveID3v1(' . $this->filename . ') because filesize (' . $this->filesize . ') is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
             return false;
         }
         if ($fp_source = fopen($this->filename, 'r+b')) {
             fseek($fp_source, -128, SEEK_END);
             if (fread($fp_source, 3) == 'TAG') {
                 ftruncate($fp_source, $this->filesize - 128);
             } else {
                 // no ID3v1 tag to begin with - do nothing
             }
             fclose($fp_source);
             return true;
         } else {
             $this->errors[] = 'Could not fopen(' . $this->filename . ', "r+b")';
         }
     } else {
         $this->errors[] = $this->filename . ' is not writeable';
     }
     return false;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     if (!getid3_lib::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for ID3v1 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $this->fseek(-256, SEEK_END);
     $preid3v1 = $this->fread(128);
     $id3v1tag = $this->fread(128);
     if (substr($id3v1tag, 0, 3) == 'TAG') {
         $info['avdataend'] = $info['filesize'] - 128;
         $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
         $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
         $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
         $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
         $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30);
         // can't remove nulls yet, track detection depends on them
         $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
         // If second-last byte of comment field is null and last byte of comment field is non-null
         // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
         if ($id3v1tag[125] === "" && $id3v1tag[126] !== "") {
             $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
             $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
         }
         $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
         $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
         if (!empty($ParsedID3v1['genre'])) {
             unset($ParsedID3v1['genreid']);
         }
         if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || $ParsedID3v1['genre'] == 'Unknown')) {
             unset($ParsedID3v1['genre']);
         }
         foreach ($ParsedID3v1 as $key => $value) {
             $ParsedID3v1['comments'][$key][0] = $value;
         }
         // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
         $GoodFormatID3v1tag = $this->GenerateID3v1Tag($ParsedID3v1['title'], $ParsedID3v1['artist'], $ParsedID3v1['album'], $ParsedID3v1['year'], isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false, $ParsedID3v1['comment'], !empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '');
         $ParsedID3v1['padding_valid'] = true;
         if ($id3v1tag !== $GoodFormatID3v1tag) {
             $ParsedID3v1['padding_valid'] = false;
             $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
         }
         $ParsedID3v1['tag_offset_end'] = $info['filesize'];
         $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
         $info['id3v1'] = $ParsedID3v1;
     }
     if (substr($preid3v1, 0, 3) == 'TAG') {
         // The way iTunes handles tags is, well, brain-damaged.
         // It completely ignores v1 if ID3v2 is present.
         // This goes as far as adding a new v1 tag *even if there already is one*
         // A suspected double-ID3v1 tag has been detected, but it could be that
         // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
         if (substr($preid3v1, 96, 8) == 'APETAGEX') {
             // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
         } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
             // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
         } else {
             // APE and Lyrics3 footers not found - assume double ID3v1
             $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
             $info['avdataend'] -= 128;
         }
     }
     return true;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     // shortcut
     $info['bonk'] = array();
     $thisfile_bonk =& $info['bonk'];
     $thisfile_bonk['dataoffset'] = $info['avdataoffset'];
     $thisfile_bonk['dataend'] = $info['avdataend'];
     if (!getid3_lib::intValueSupported($thisfile_bonk['dataend'])) {
         $info['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to ' . round(PHP_INT_MAX / 1073741824) . 'GB';
     } else {
         // scan-from-end method, for v0.6 and higher
         fseek($this->getid3->fp, $thisfile_bonk['dataend'] - 8, SEEK_SET);
         $PossibleBonkTag = fread($this->getid3->fp, 8);
         while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
             $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
             fseek($this->getid3->fp, 0 - $BonkTagSize, SEEK_CUR);
             $BonkTagOffset = ftell($this->getid3->fp);
             $TagHeaderTest = fread($this->getid3->fp, 5);
             if ($TagHeaderTest[0] != "" || substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4))) {
                 $info['error'][] = 'Expecting "' . getid3_lib::PrintHexBytes("" . strtoupper(substr($PossibleBonkTag, 4, 4))) . '" at offset ' . $BonkTagOffset . ', found "' . getid3_lib::PrintHexBytes($TagHeaderTest) . '"';
                 return false;
             }
             $BonkTagName = substr($TagHeaderTest, 1, 4);
             $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
             $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
             $this->HandleBonkTags($BonkTagName);
             $NextTagEndOffset = $BonkTagOffset - 8;
             if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
                 if (empty($info['audio']['encoder'])) {
                     $info['audio']['encoder'] = 'Extended BONK v0.9+';
                 }
                 return true;
             }
             fseek($this->getid3->fp, $NextTagEndOffset, SEEK_SET);
             $PossibleBonkTag = fread($this->getid3->fp, 8);
         }
     }
     // seek-from-beginning method for v0.4 and v0.5
     if (empty($thisfile_bonk['BONK'])) {
         fseek($this->getid3->fp, $thisfile_bonk['dataoffset'], SEEK_SET);
         do {
             $TagHeaderTest = fread($this->getid3->fp, 5);
             switch ($TagHeaderTest) {
                 case "" . 'BONK':
                     if (empty($info['audio']['encoder'])) {
                         $info['audio']['encoder'] = 'BONK v0.4';
                     }
                     break;
                 case "" . 'INFO':
                     $info['audio']['encoder'] = 'Extended BONK v0.5';
                     break;
                 default:
                     break 2;
             }
             $BonkTagName = substr($TagHeaderTest, 1, 4);
             $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
             $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
             $this->HandleBonkTags($BonkTagName);
         } while (true);
     }
     // parse META block for v0.6 - v0.8
     if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
         fseek($this->getid3->fp, $thisfile_bonk['META']['tags']['info'], SEEK_SET);
         $TagHeaderTest = fread($this->getid3->fp, 5);
         if ($TagHeaderTest == "" . 'INFO') {
             $info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
             $BonkTagName = substr($TagHeaderTest, 1, 4);
             $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
             $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
             $this->HandleBonkTags($BonkTagName);
         }
     }
     if (empty($info['audio']['encoder'])) {
         $info['audio']['encoder'] = 'Extended BONK v0.9+';
     }
     if (empty($thisfile_bonk['BONK'])) {
         unset($info['bonk']);
     }
     return true;
 }
 function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     $info =& $this->getid3->info;
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
     // http://wiki.multimedia.cx/index.php?title=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // MPEG layer                                      2    If you send AAC in MPEG-TS, set to 0
     // protection_absent                               1    0: CRC present; 1: no CRC
     // profile                                         2    0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
     // sampling_frequency_index                        4    15 not allowed
     // private_bit                                     1    usually 0
     // channel_configuration                           3
     // original/copy                                   1    0: original; 1: copy
     // home                                            1    usually 0
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)  // not present in some documentation?
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = $info['avdataoffset'];
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; $i++) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!getid3_lib::intValueSupported($byteoffset)) {
             $info['warning'][] = 'Unable to parse AAC file beyond ' . ftell($this->getid3->fp) . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         fseek($this->getid3->fp, $byteoffset, SEEK_SET);
         // First get substring
         $substring = fread($this->getid3->fp, 9);
         // header is 7 bytes (or 9 if CRC is present)
         $substringlength = strlen($substring);
         if ($substringlength != 9) {
             $info['error'][] = 'Failed to read 7 bytes at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // this would be easier with 64-bit math, but split it up to allow for 32-bit:
         $header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
         $header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
         $header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
         $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4;
         if ($info['aac']['header']['raw']['syncword'] != 0xfff) {
             $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)';
             //if ($info['fileformat'] == 'aac') {
             //	return true;
             //}
             unset($info['aac']);
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber == 0) {
             $info['aac']['header_type'] = 'ADTS';
             $info['fileformat'] = 'aac';
             $info['audio']['dataformat'] = 'aac';
             $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3;
             $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1;
             $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0;
             $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30;
             $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26;
             $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25;
             $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22;
             $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21;
             $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20;
             $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19;
             $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18;
             $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5;
             $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4;
             $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true;
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
             $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
             $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
             $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
             $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
             $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code'];
             if ($ReturnExtendedInfo) {
                 $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
                 $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
             }
             if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
                 $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead';
             }
             if ($info['aac']['header']['sample_frequency'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
             $info['audio']['channels'] = $info['aac']['header']['channels'];
         }
         $FrameLength = ($header2 & 0x3ffe0) >> 5;
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2;
         if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $info['audio']['bitrate_mode'] = 'vbr';
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
         }
         $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0;
         if ($info['aac']['header']['crc_present']) {
             //$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
         }
         if (!$ReturnExtendedInfo) {
             unset($info['aac'][$framenumber]);
         }
         /*
         $rounded_precision = 5000;
         $info['aac']['bitrate_distribution_rounded'] = array();
         foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
         	$rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
         	getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
         }
         ksort($info['aac']['bitrate_distribution_rounded']);
         */
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) {
             // keep scanning
         } else {
             $info['aac']['frames'] = $framenumber;
             $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             ksort($info['aac']['bitrate_distribution']);
             $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
             return true;
         }
     }
     // should never get here.
 }
Пример #8
0
 function analyze($filename)
 {
     try {
         if (!empty($this->startup_error)) {
             return $this->error($this->startup_error);
         }
         if (!empty($this->startup_warning)) {
             $this->warning($this->startup_warning);
         }
         // init result array and set parameters
         $this->info = array();
         $this->info['GETID3_VERSION'] = GETID3_VERSION;
         // Check encoding/iconv support
         if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
             $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
             if (GETID3_OS_ISWINDOWS) {
                 $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
             } else {
                 $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
             }
             return $this->error($errormessage);
         }
         // remote files not supported
         if (preg_match('/^(ht|f)tp:\\/\\//', $filename)) {
             return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
         }
         $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
         $filename = preg_replace('#(.+)' . preg_quote(DIRECTORY_SEPARATOR) . '{2,}#U', '\\1' . DIRECTORY_SEPARATOR, $filename);
         // open local file
         if (file_exists($filename) && is_file($filename)) {
             ob_start();
             if ($fp = fopen($filename, 'rb')) {
                 // great
                 ob_end_clean();
             } else {
                 $fopen_error = ob_get_contents();
                 ob_end_clean();
                 return $this->error('Could not open file "' . $filename . '" (fopen says: ' . $fopen_error . ')');
             }
         } else {
             return $this->error('Could not open "' . $filename . '" (does not exist, or is not a file)');
         }
         // set parameters
         // $this->info['filesize'] = filesize($filename);
         $this->info['filesize'] = sprintf("%u", filesize($filename));
         // option_max_2gb_check
         if ($this->option_max_2gb_check) {
             // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
             // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
             // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
             $fseek = fseek($fp, 0, SEEK_END);
             if ($fseek < 0 || $this->info['filesize'] != 0 && ftell($fp) == 0 || $this->info['filesize'] < 0 || ftell($fp) < 0) {
                 $real_filesize = false;
                 if (GETID3_OS_ISWINDOWS) {
                     $commandline = 'dir /-C "' . str_replace('/', DIRECTORY_SEPARATOR, $filename) . '"';
                     $dir_output = `{$commandline}`;
                     if (preg_match('#1 File\\(s\\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
                         $real_filesize = (double) $matches[1];
                     }
                 } else {
                     $commandline = 'ls -o -g -G --time-style=long-iso ' . escapeshellarg($filename);
                     $dir_output = `{$commandline}`;
                     if (preg_match('#([0-9]+) ([0-9]{4}-[0-9]{2}\\-[0-9]{2} [0-9]{2}:[0-9]{2}) ' . str_replace('#', '\\#', preg_quote($filename)) . '$#', $dir_output, $matches)) {
                         $real_filesize = (double) $matches[1];
                     }
                 }
                 if ($real_filesize === false) {
                     unset($this->info['filesize']);
                     fclose($fp);
                     return $this->error('Unable to determine actual filesize. File is most likely larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB and is not supported by PHP.');
                 } elseif (getid3_lib::intValueSupported($real_filesize)) {
                     unset($this->info['filesize']);
                     fclose($fp);
                     return $this->error('PHP seems to think the file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB, but filesystem reports it as ' . number_format($real_filesize, 3) . 'GB, please report to info@getid3.org');
                 }
                 $this->info['filesize'] = $real_filesize;
                 $this->error('File is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB (filesystem reports it as ' . number_format($real_filesize, 3) . 'GB) and is not properly supported by PHP.');
             }
         }
         // set more parameters
         $this->info['avdataoffset'] = 0;
         $this->info['avdataend'] = $this->info['filesize'];
         $this->info['fileformat'] = '';
         // filled in later
         $this->info['audio']['dataformat'] = '';
         // filled in later, unset if not used
         $this->info['video']['dataformat'] = '';
         // filled in later, unset if not used
         $this->info['tags'] = array();
         // filled in later, unset if not used
         $this->info['error'] = array();
         // filled in later, unset if not used
         $this->info['warning'] = array();
         // filled in later, unset if not used
         $this->info['comments'] = array();
         // filled in later, unset if not used
         $this->info['encoding'] = $this->encoding;
         // required by id3v2 and iso modules - can be unset at the end if desired
         // set redundant parameters - might be needed in some include file
         $this->info['filename'] = basename($filename);
         $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
         $this->info['filenamepath'] = $this->info['filepath'] . '/' . $this->info['filename'];
         // handle ID3v2 tag - done first - already at beginning of file
         // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
         if ($this->option_tag_id3v2) {
             $GETID3_ERRORARRAY =& $this->info['warning'];
             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.tag.id3v2.php', __FILE__, false)) {
                 $tag = new getid3_id3v2($fp, $this->info);
                 unset($tag);
             }
         } else {
             fseek($fp, 0, SEEK_SET);
             $header = fread($fp, 10);
             if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
                 $this->info['id3v2']['header'] = true;
                 $this->info['id3v2']['majorversion'] = ord($header[3]);
                 $this->info['id3v2']['minorversion'] = ord($header[4]);
                 $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10;
                 // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
                 $this->info['id3v2']['tag_offset_start'] = 0;
                 $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
                 $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end'];
             }
         }
         // handle ID3v1 tag
         if ($this->option_tag_id3v1) {
             if (!file_exists(GETID3_INCLUDEPATH . 'module.tag.id3v1.php') || !(include_once GETID3_INCLUDEPATH . 'module.tag.id3v1.php')) {
                 return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
             }
             $tag = new getid3_id3v1($fp, $this->info);
             unset($tag);
         }
         // handle APE tag
         if ($this->option_tag_apetag) {
             if (!file_exists(GETID3_INCLUDEPATH . 'module.tag.apetag.php') || !(include_once GETID3_INCLUDEPATH . 'module.tag.apetag.php')) {
                 return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
             }
             $tag = new getid3_apetag($fp, $this->info);
             unset($tag);
         }
         // handle lyrics3 tag
         if ($this->option_tag_lyrics3) {
             if (!file_exists(GETID3_INCLUDEPATH . 'module.tag.lyrics3.php') || !(include_once GETID3_INCLUDEPATH . 'module.tag.lyrics3.php')) {
                 return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
             }
             $tag = new getid3_lyrics3($fp, $this->info);
             unset($tag);
         }
         // read 32 kb file data
         fseek($fp, $this->info['avdataoffset'], SEEK_SET);
         $formattest = fread($fp, 32774);
         // determine format
         $determined_format = $this->GetFileFormat($formattest, $filename);
         // unable to determine file format
         if (!$determined_format) {
             fclose($fp);
             return $this->error('unable to determine file format');
         }
         // check for illegal ID3 tags
         if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
             if ($determined_format['fail_id3'] === 'ERROR') {
                 fclose($fp);
                 return $this->error('ID3 tags not allowed on this file type.');
             } elseif ($determined_format['fail_id3'] === 'WARNING') {
                 $this->info['warning'][] = 'ID3 tags not allowed on this file type.';
             }
         }
         // check for illegal APE tags
         if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
             if ($determined_format['fail_ape'] === 'ERROR') {
                 fclose($fp);
                 return $this->error('APE tags not allowed on this file type.');
             } elseif ($determined_format['fail_ape'] === 'WARNING') {
                 $this->info['warning'][] = 'APE tags not allowed on this file type.';
             }
         }
         // set mime type
         $this->info['mime_type'] = $determined_format['mime_type'];
         // supported format signature pattern detected, but module deleted
         if (!file_exists(GETID3_INCLUDEPATH . $determined_format['include'])) {
             fclose($fp);
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" was removed.');
         }
         // module requires iconv support
         if (!function_exists('iconv') && !empty($determined_format['iconv_req'])) {
             return $this->error('iconv support is required for this module (' . $determined_format['include'] . ').');
         }
         // include module
         include_once GETID3_INCLUDEPATH . $determined_format['include'];
         // instantiate module class
         $class_name = 'getid3_' . $determined_format['module'];
         if (!class_exists($class_name)) {
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" is corrupt.');
         }
         if (isset($determined_format['option'])) {
             $class = new $class_name($fp, $this->info, $determined_format['option']);
         } else {
             $class = new $class_name($fp, $this->info);
         }
         unset($class);
         // close file
         fclose($fp);
         // process all tags - copy to 'tags' and convert charsets
         if ($this->option_tags_process) {
             $this->HandleAllTags();
         }
         // perform more calculations
         if ($this->option_extra_info) {
             $this->ChannelsBitratePlaytimeCalculations();
             $this->CalculateCompressionRatioVideo();
             $this->CalculateCompressionRatioAudio();
             $this->CalculateReplayGain();
             $this->ProcessAudioStreams();
         }
         // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->option_md5_data) {
             // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
             if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
                 $this->getHashdata('md5');
             }
         }
         // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->option_sha1_data) {
             $this->getHashdata('sha1');
         }
         // remove undesired keys
         $this->CleanUp();
     } catch (Exception $e) {
         if (isset($this->info['error'])) {
             $this->info['error'][] = 'Caught exception: ' . $e->getMessage();
         } else {
             $this->info['error'] = array('Caught exception: ' . $e->getMessage());
         }
     }
     // return info array
     return $this->info;
 }
Пример #9
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     if (!getid3_lib::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for ID3v1 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $this->fseek(-256, SEEK_END);
     $preid3v1 = $this->fread(128);
     $id3v1tag = $this->fread(128);
     if (substr($id3v1tag, 0, 3) == 'TAG') {
         $info['avdataend'] = $info['filesize'] - 128;
         $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
         $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
         $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
         $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
         $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30);
         // can't remove nulls yet, track detection depends on them
         $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
         // If second-last byte of comment field is null and last byte of comment field is non-null
         // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
         if ($id3v1tag[125] === "" && $id3v1tag[126] !== "") {
             $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
             $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
         }
         $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
         $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
         if (!empty($ParsedID3v1['genre'])) {
             unset($ParsedID3v1['genreid']);
         }
         if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || $ParsedID3v1['genre'] == 'Unknown')) {
             unset($ParsedID3v1['genre']);
         }
         foreach ($ParsedID3v1 as $key => $value) {
             $ParsedID3v1['comments'][$key][0] = $value;
         }
         // ID3v1 encoding detection hack START
         // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
         // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
         $ID3v1encoding = 'ISO-8859-1';
         foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
             foreach ($valuearray as $key => $value) {
                 if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
                     foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
                         if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
                             $ID3v1encoding = $id3v1_bad_encoding;
                             break 3;
                         } elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
                             $ID3v1encoding = $id3v1_bad_encoding;
                             break 3;
                         }
                     }
                 }
             }
         }
         // ID3v1 encoding detection hack END
         // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
         $GoodFormatID3v1tag = $this->GenerateID3v1Tag($ParsedID3v1['title'], $ParsedID3v1['artist'], $ParsedID3v1['album'], $ParsedID3v1['year'], isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false, $ParsedID3v1['comment'], !empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '');
         $ParsedID3v1['padding_valid'] = true;
         if ($id3v1tag !== $GoodFormatID3v1tag) {
             $ParsedID3v1['padding_valid'] = false;
             $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
         }
         $ParsedID3v1['tag_offset_end'] = $info['filesize'];
         $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
         $info['id3v1'] = $ParsedID3v1;
         $info['id3v1']['encoding'] = $ID3v1encoding;
     }
     if (substr($preid3v1, 0, 3) == 'TAG') {
         // The way iTunes handles tags is, well, brain-damaged.
         // It completely ignores v1 if ID3v2 is present.
         // This goes as far as adding a new v1 tag *even if there already is one*
         // A suspected double-ID3v1 tag has been detected, but it could be that
         // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
         if (substr($preid3v1, 96, 8) == 'APETAGEX') {
             // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
         } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
             // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
         } else {
             // APE and Lyrics3 footers not found - assume double ID3v1
             $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
             $info['avdataend'] -= 128;
         }
     }
     return true;
 }
 function ParseRIFF($startoffset, $maxoffset)
 {
     $info =& $this->getid3->info;
     $maxoffset = min($maxoffset, $info['avdataend']);
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) {
         $info['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
         return false;
     }
     $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset);
     if ($maxoffset > $max_usable_offset) {
         $info['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
     }
     fseek($this->getid3->fp, $startoffset, SEEK_SET);
     while (ftell($this->getid3->fp) < $max_usable_offset) {
         $chunknamesize = fread($this->getid3->fp, 8);
         $chunkname = substr($chunknamesize, 0, 4);
         $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
         if (strlen($chunkname) < 4) {
             $info['error'][] = 'Expecting chunk name at offset ' . (ftell($this->getid3->fp) - 4) . ' but found nothing. Aborting RIFF parsing.';
             break;
         }
         if ($chunksize == 0) {
             if ($chunkname == 'JUNK') {
                 // we'll allow zero-size JUNK frames
             } else {
                 $info['warning'][] = 'Chunk size at offset ' . (ftell($this->getid3->fp) - 4) . ' is zero. Aborting RIFF parsing.';
                 break;
             }
         }
         if ($chunksize % 2 != 0) {
             // all structures are packed on word boundaries
             $chunksize++;
         }
         switch ($chunkname) {
             case 'LIST':
                 $listname = fread($this->getid3->fp, 4);
                 if (preg_match('#^(movi|rec )$#i', $listname)) {
                     $RIFFchunk[$listname]['offset'] = ftell($this->getid3->fp) - 4;
                     $RIFFchunk[$listname]['size'] = $chunksize;
                     if ($FoundAllChunksWeNeed) {
                         // skip over
                     } else {
                         $WhereWeWere = ftell($this->getid3->fp);
                         $AudioChunkHeader = fread($this->getid3->fp, 12);
                         $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                         $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                         $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                         if ($AudioChunkStreamType == 'wb') {
                             $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                             if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                 // MP3
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
                                     $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
                                     $getid3_mp3 = new getid3_mp3($getid3_temp);
                                     $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                                     if (isset($getid3_temp->info['mpeg']['audio'])) {
                                         $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                         $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                         $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                         $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                         $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                         //$info['bitrate']               = $info['audio']['bitrate'];
                                     }
                                     unset($getid3_temp, $getid3_mp3);
                                 }
                             } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) {
                                 // AC3
                                 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
                                     $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
                                     $getid3_ac3 = new getid3_ac3($getid3_temp);
                                     $getid3_ac3->Analyze();
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['ac3'] = $getid3_temp->info['ac3'];
                                         if (!empty($getid3_temp->info['warning'])) {
                                             foreach ($getid3_temp->info['warning'] as $key => $value) {
                                                 $info['warning'][] = $value;
                                             }
                                         }
                                     }
                                     unset($getid3_temp, $getid3_ac3);
                                 }
                             }
                         }
                         $FoundAllChunksWeNeed = true;
                         fseek($this->getid3->fp, $WhereWeWere, SEEK_SET);
                     }
                     fseek($this->getid3->fp, $chunksize - 4, SEEK_CUR);
                     //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) {
                     //
                     //	// data chunk, ignore
                     //
                 } else {
                     if (!isset($RIFFchunk[$listname])) {
                         $RIFFchunk[$listname] = array();
                     }
                     $LISTchunkParent = $listname;
                     $LISTchunkMaxOffset = ftell($this->getid3->fp) - 4 + $chunksize;
                     if ($parsedChunk = $this->ParseRIFF(ftell($this->getid3->fp), ftell($this->getid3->fp) + $chunksize - 4)) {
                         $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                     }
                 }
                 break;
             default:
                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                     $nextoffset = ftell($this->getid3->fp) + $chunksize;
                     if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                         $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                         break 2;
                     }
                     fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                     break;
                 }
                 $thisindex = 0;
                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                     $thisindex = count($RIFFchunk[$chunkname]);
                 }
                 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($this->getid3->fp) - 8;
                 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                 switch ($chunkname) {
                     case 'data':
                         $info['avdataoffset'] = ftell($this->getid3->fp);
                         $info['avdataend'] = $info['avdataoffset'] + $chunksize;
                         $RIFFdataChunkContentsTest = fread($this->getid3->fp, 36);
                         if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                             // Probably is MP3 data
                             if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $getid3_mp3 = new getid3_mp3($getid3_temp);
                                 $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false);
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['mpeg'] = $getid3_temp->info['mpeg'];
                                     $info['audio'] = $getid3_temp->info['audio'];
                                 }
                                 unset($getid3_temp, $getid3_mp3);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") {
                             // This is probably AC-3 data
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
                                 $getid3_ac3->Analyze();
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['audio'] = $getid3_temp->info['audio'];
                                     $info['ac3'] = $getid3_temp->info['ac3'];
                                     $info['warning'] = $getid3_temp->info['warning'];
                                 }
                                 unset($getid3_temp, $getid3_ac3);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") {
                             // Dolby Digital WAV
                             // AC-3 content, but not encoded in same format as normal AC-3 file
                             // For one thing, byte order is swapped
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 // ok to use tmpfile here - only 56 bytes
                                 if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
                                     if ($fd_temp = fopen($RIFFtempfilename, 'wb')) {
                                         for ($i = 0; $i < 28; $i += 2) {
                                             // swap byte order
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                         }
                                         fclose($fd_temp);
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($RIFFtempfilename);
                                         $getid3_temp->info['avdataend'] = 20;
                                         $getid3_ac3 = new getid3_ac3($getid3_temp);
                                         $getid3_ac3->Analyze();
                                         if (empty($getid3_temp->info['error'])) {
                                             $info['audio'] = $getid3_temp->info['audio'];
                                             $info['ac3'] = $getid3_temp->info['ac3'];
                                             $info['warning'] = $getid3_temp->info['warning'];
                                         } else {
                                             $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): ' . implode(';', $getid3_temp->info['error']);
                                         }
                                         unset($getid3_ac3, $getid3_temp);
                                     } else {
                                         $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                     }
                                     unlink($RIFFtempfilename);
                                 } else {
                                     $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                             $this->RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28));
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($this->getid3->fp, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
                         break;
                     case 'iXML':
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'strh':
                     case 'strf':
                     case 'indx':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                     // always read data in
                     case 'JUNK':
                         // should be: never read data in
                         // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
                         if ($chunksize < 1048576) {
                             if ($chunksize > 0) {
                                 $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                                 if ($chunkname == 'JUNK') {
                                     if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
                                         // only keep text characters [chr(32)-chr(127)]
                                         $info['riff']['comments']['junk'][] = trim($matches[1]);
                                     }
                                     // but if nothing there, ignore
                                     // remove the key in either case
                                     unset($RIFFchunk[$chunkname][$thisindex]['data']);
                                 }
                             }
                         } else {
                             $info['warning'][] = 'chunk "' . $chunkname . '" at offset ' . ftell($this->getid3->fp) . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data';
                             $nextoffset = ftell($this->getid3->fp) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                         }
                         break;
                     default:
                         if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                             unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                             unset($RIFFchunk[$chunkname][$thisindex]['size']);
                             if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                 unset($RIFFchunk[$chunkname][$thisindex]);
                             }
                             if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                 unset($RIFFchunk[$chunkname]);
                             }
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                             //} elseif (in_array($chunkname, array('ID3 ')) || (($chunksize > 0) && ($chunksize < 2048))) {
                         } elseif ($chunksize > 0 && $chunksize < 2048) {
                             // only read data in if smaller than 2kB
                             $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                         } else {
                             $nextoffset = ftell($this->getid3->fp) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                         }
                         break;
                 }
                 break;
         }
     }
     return $RIFFchunk;
 }
Пример #11
0
 public function RemoveID3v2()
 {
     // File MUST be writeable - CHMOD(646) at least. It's best if the
     // directory is also writeable, because that method is both faster and less susceptible to errors.
     if (is_writeable(dirname($this->filename))) {
         // preferred method - only one copying operation, minimal chance of corrupting
         // original file if script is interrupted, but required directory to be writeable
         if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
             // Initialize getID3 engine
             $getID3 = new getID3();
             $OldThisFileInfo = $getID3->analyze($this->filename);
             if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
                 $this->errors[] = 'Unable to remove ID3v2 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
                 fclose($fp_source);
                 return false;
             }
             rewind($fp_source);
             if ($OldThisFileInfo['avdataoffset'] !== false) {
                 fseek($fp_source, $OldThisFileInfo['avdataoffset']);
             }
             if (is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename . 'getid3tmp', 'w+b'))) {
                 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
                     fwrite($fp_temp, $buffer, strlen($buffer));
                 }
                 fclose($fp_temp);
             } else {
                 $this->errors[] = 'Could not fopen("' . $this->filename . 'getid3tmp", "w+b")';
             }
             fclose($fp_source);
         } else {
             $this->errors[] = 'Could not fopen("' . $this->filename . '", "rb")';
         }
         if (file_exists($this->filename)) {
             unlink($this->filename);
         }
         rename($this->filename . 'getid3tmp', $this->filename);
     } elseif (is_writable($this->filename)) {
         // less desirable alternate method - double-copies the file, overwrites original file
         // and could corrupt source file if the script is interrupted or an error occurs.
         if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
             // Initialize getID3 engine
             $getID3 = new getID3();
             $OldThisFileInfo = $getID3->analyze($this->filename);
             if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
                 $this->errors[] = 'Unable to remove ID3v2 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
                 fclose($fp_source);
                 return false;
             }
             rewind($fp_source);
             if ($OldThisFileInfo['avdataoffset'] !== false) {
                 fseek($fp_source, $OldThisFileInfo['avdataoffset']);
             }
             if ($fp_temp = tmpfile()) {
                 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
                     fwrite($fp_temp, $buffer, strlen($buffer));
                 }
                 fclose($fp_source);
                 if (is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'wb'))) {
                     rewind($fp_temp);
                     while ($buffer = fread($fp_temp, $this->fread_buffer_size)) {
                         fwrite($fp_source, $buffer, strlen($buffer));
                     }
                     fseek($fp_temp, -128, SEEK_END);
                     fclose($fp_source);
                 } else {
                     $this->errors[] = 'Could not fopen("' . $this->filename . '", "wb")';
                 }
                 fclose($fp_temp);
             } else {
                 $this->errors[] = 'Could not create tmpfile()';
             }
         } else {
             $this->errors[] = 'Could not fopen("' . $this->filename . '", "rb")';
         }
     } else {
         $this->errors[] = 'Directory and file both not writeable';
     }
     if (!empty($this->errors)) {
         return false;
     }
     return true;
 }
 function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData = true, $ParseAllPossibleAtoms = false)
 {
     $ThisFileInfo['fileformat'] = 'quicktime';
     $ThisFileInfo['quicktime']['hinting'] = false;
     $ThisFileInfo['quicktime']['controller'] = 'standard';
     // may be overridden if 'ctyp' atom is present
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $offset = 0;
     $atomcounter = 0;
     while ($offset < $ThisFileInfo['avdataend']) {
         if (!getid3_lib::intValueSupported($offset)) {
             $ThisFileInfo['error'][] = 'Unable to parse atom at offset ' . $offset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
             break;
         }
         fseek($fd, $offset, SEEK_SET);
         $AtomHeader = fread($fd, 8);
         $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
         $atomname = substr($AtomHeader, 4, 4);
         // 64-bit MOV patch by jlegateØktnc*com
         if ($atomsize == 1) {
             $atomsize = getid3_lib::BigEndian2Int(fread($fd, 8));
         }
         $ThisFileInfo['quicktime'][$atomname]['name'] = $atomname;
         $ThisFileInfo['quicktime'][$atomname]['size'] = $atomsize;
         $ThisFileInfo['quicktime'][$atomname]['offset'] = $offset;
         if ($offset + $atomsize > $ThisFileInfo['avdataend']) {
             $ThisFileInfo['error'][] = 'Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atomsize . ' bytes)';
             return false;
         }
         if ($atomsize == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         switch ($atomname) {
             case 'mdat':
                 // Media DATa atom
                 // 'mdat' contains the actual data for the audio/video
                 if ($atomsize > 8 && (!isset($ThisFileInfo['avdataend_tmp']) || $ThisFileInfo['quicktime'][$atomname]['size'] > $ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])) {
                     $ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8;
                     $OldAVDataEnd = $ThisFileInfo['avdataend'];
                     $ThisFileInfo['avdataend'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size'];
                     if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) {
                         getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false);
                         if (isset($ThisFileInfo['mpeg']['audio'])) {
                             $ThisFileInfo['audio']['dataformat'] = 'mp3';
                             $ThisFileInfo['audio']['codec'] = !empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                             $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
                             $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
                             $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
                             $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
                             $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate'];
                         }
                     }
                     $ThisFileInfo['avdataend'] = $OldAVDataEnd;
                     unset($OldAVDataEnd);
                 }
                 break;
             case 'free':
                 // FREE space atom
             // FREE space atom
             case 'skip':
                 // SKIP atom
             // SKIP atom
             case 'wide':
                 // 64-bit expansion placeholder atom
                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
                 break;
             default:
                 $atomHierarchy = array();
                 $ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms);
                 break;
         }
         $offset += $atomsize;
         $atomcounter++;
     }
     if (!empty($ThisFileInfo['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp'];
         unset($ThisFileInfo['avdataend_tmp']);
     }
     if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) {
         $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
     }
     if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) {
         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate'];
     }
     if (!empty($ThisFileInfo['playtime_seconds']) && !isset($ThisFileInfo['video']['frame_rate']) && !empty($ThisFileInfo['quicktime']['stts_framecount'])) {
         foreach ($ThisFileInfo['quicktime']['stts_framecount'] as $key => $samples_count) {
             $samples_per_second = $samples_count / $ThisFileInfo['playtime_seconds'];
             if ($samples_per_second > 240) {
                 // has to be audio samples
             } else {
                 $ThisFileInfo['video']['frame_rate'] = $samples_per_second;
                 break;
             }
         }
     }
     if ($ThisFileInfo['audio']['dataformat'] == 'mp4' && empty($ThisFileInfo['video']['resolution_x'])) {
         $ThisFileInfo['fileformat'] = 'mp4';
         $ThisFileInfo['mime_type'] = 'audio/mp4';
         unset($ThisFileInfo['video']['dataformat']);
     }
     if (!$ReturnAtomData) {
         unset($ThisFileInfo['quicktime']['moov']);
     }
     if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) {
         $ThisFileInfo['audio']['dataformat'] = 'quicktime';
     }
     if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) {
         $ThisFileInfo['video']['dataformat'] = 'quicktime';
     }
     return true;
 }
Пример #13
0
 static function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo)
 {
     $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']);
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) {
         $ThisFileInfo['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
         return false;
     }
     $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset);
     if ($maxoffset > $max_usable_offset) {
         $ThisFileInfo['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
     }
     fseek($fd, $startoffset, SEEK_SET);
     while (ftell($fd) < $max_usable_offset) {
         $chunknamesize = fread($fd, 8);
         $chunkname = substr($chunknamesize, 0, 4);
         $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($chunknamesize, 4, 4));
         if (strlen($chunkname) < 4) {
             $ThisFileInfo['error'][] = 'Expecting chunk name at offset ' . (ftell($fd) - 4) . ' but found nothing. Aborting RIFF parsing.';
             break;
         }
         if ($chunksize == 0) {
             if ($chunkname == 'JUNK') {
                 // we'll allow zero-size JUNK frames
             } else {
                 $ThisFileInfo['warning'][] = 'Chunk size at offset ' . (ftell($fd) - 4) . ' is zero. Aborting RIFF parsing.';
                 break;
             }
         }
         if ($chunksize % 2 != 0) {
             // all structures are packed on word boundaries
             $chunksize++;
         }
         switch ($chunkname) {
             case 'LIST':
                 $listname = fread($fd, 4);
                 if (preg_match('#^(movi|rec )$#i', $listname)) {
                     $RIFFchunk[$listname]['offset'] = ftell($fd) - 4;
                     $RIFFchunk[$listname]['size'] = $chunksize;
                     if ($FoundAllChunksWeNeed) {
                         // skip over
                     } else {
                         $WhereWeWere = ftell($fd);
                         $AudioChunkHeader = fread($fd, 12);
                         $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                         $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                         $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                         if ($AudioChunkStreamType == 'wb') {
                             $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                             if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                 // MP3
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = ftell($fd) - 4;
                                     $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                     getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false);
                                     if (isset($dummy['mpeg']['audio'])) {
                                         $ThisFileInfo = $dummy;
                                         $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer'];
                                         $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
                                         $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
                                         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
                                         $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate'];
                                         $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
                                     }
                                     unset($dummy);
                                 }
                             } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) {
                                 // AC3
                                 $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                                 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = ftell($fd) - 4;
                                     $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                     $dummy['error'] = array();
                                     $ac3_tag = new getid3_ac3($fd, $dummy);
                                     if (empty($dummy['error'])) {
                                         $ThisFileInfo['audio'] = $dummy['audio'];
                                         $ThisFileInfo['ac3'] = $dummy['ac3'];
                                         $ThisFileInfo['warning'] = $dummy['warning'];
                                     }
                                     unset($ac3_tag);
                                 }
                             }
                         }
                         $FoundAllChunksWeNeed = true;
                         fseek($fd, $WhereWeWere, SEEK_SET);
                     }
                     fseek($fd, $chunksize - 4, SEEK_CUR);
                     //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) {
                     //
                     //	// data chunk, ignore
                     //
                 } else {
                     if (!isset($RIFFchunk[$listname])) {
                         $RIFFchunk[$listname] = array();
                     }
                     $LISTchunkParent = $listname;
                     $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize;
                     if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) {
                         $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                     }
                 }
                 break;
             default:
                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                     $nextoffset = ftell($fd) + $chunksize;
                     if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                         $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                         break 2;
                     }
                     fseek($fd, $nextoffset, SEEK_SET);
                     break;
                 }
                 $thisindex = 0;
                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                     $thisindex = count($RIFFchunk[$chunkname]);
                 }
                 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8;
                 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                 switch ($chunkname) {
                     case 'data':
                         $ThisFileInfo['avdataoffset'] = ftell($fd);
                         $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize;
                         $RIFFdataChunkContentsTest = fread($fd, 36);
                         if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                             // Probably is MP3 data
                             if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 $dummy = $ThisFileInfo;
                                 // copy info array, only use if there's no error
                                 $getid3_mp3 = new getid3_mp3($fd, $dummy);
                                 $dummy = $ThisFileInfo;
                                 // copy info array, only use if there's no error
                                 $getid3_mp3->getOnlyMPEGaudioInfo($fd, $dummy, $RIFFchunk[$chunkname][$thisindex]['offset'], false);
                                 // use dummy array unless error
                                 if (empty($dummy['error'])) {
                                     $ThisFileInfo = $dummy;
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") {
                             // This is probably AC-3 data
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 $dummy = $ThisFileInfo;
                                 $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $dummy['error'] = array();
                                 $ac3_tag = new getid3_ac3($fd, $dummy);
                                 if (empty($dummy['error'])) {
                                     $ThisFileInfo['audio'] = $dummy['audio'];
                                     $ThisFileInfo['ac3'] = $dummy['ac3'];
                                     $ThisFileInfo['warning'] = $dummy['warning'];
                                 }
                                 unset($ac3_tag);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") {
                             // Dolby Digital WAV
                             // AC-3 content, but not encoded in same format as normal AC-3 file
                             // For one thing, byte order is swapped
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 // ok to use tmpfile here - only 56 bytes
                                 if ($fd_temp = tmpfile()) {
                                     for ($i = 0; $i < 28; $i += 2) {
                                         // swap byte order
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                     }
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = 0;
                                     $dummy['avdataend'] = 20;
                                     $dummy['error'] = array();
                                     $ac3_tag = new getid3_ac3($fd_temp, $dummy);
                                     fclose($fd_temp);
                                     if (empty($dummy['error'])) {
                                         $ThisFileInfo['audio'] = $dummy['audio'];
                                         $ThisFileInfo['ac3'] = $dummy['ac3'];
                                         $ThisFileInfo['warning'] = $dummy['warning'];
                                     } else {
                                         $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: ' . explode(';', $dummy['error']);
                                     }
                                     unset($ac3_tag);
                                 } else {
                                     $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV';
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                             getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo);
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
                         break;
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'strh':
                     case 'strf':
                     case 'indx':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                         $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         break;
                     case 'JUNK':
                         // never read data in
                         $nextoffset = ftell($fd) + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($fd, $nextoffset, SEEK_SET);
                         break;
                     default:
                         if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                             unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                             unset($RIFFchunk[$chunkname][$thisindex]['size']);
                             if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                 unset($RIFFchunk[$chunkname][$thisindex]);
                             }
                             if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                 unset($RIFFchunk[$chunkname]);
                             }
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } elseif ($chunksize > 0 && $chunksize < 2048) {
                             // only read data in if smaller than 2kB
                             $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } else {
                             $nextoffset = ftell($fd) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($fd, $nextoffset, SEEK_SET);
                         }
                         break;
                 }
                 break;
         }
     }
     return $RIFFchunk;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // initialize these values to an empty array, otherwise they default to NULL
     // and you can't append array values to a NULL value
     $info['riff'] = array('raw' => array());
     // Shortcuts
     $thisfile_riff =& $info['riff'];
     $thisfile_riff_raw =& $thisfile_riff['raw'];
     $thisfile_audio =& $info['audio'];
     $thisfile_video =& $info['video'];
     $thisfile_audio_dataformat =& $thisfile_audio['dataformat'];
     $thisfile_riff_audio =& $thisfile_riff['audio'];
     $thisfile_riff_video =& $thisfile_riff['video'];
     $Original['avdataoffset'] = $info['avdataoffset'];
     $Original['avdataend'] = $info['avdataend'];
     $this->fseek($info['avdataoffset']);
     $RIFFheader = $this->fread(12);
     $offset = $this->ftell();
     $RIFFtype = substr($RIFFheader, 0, 4);
     $RIFFsize = substr($RIFFheader, 4, 4);
     $RIFFsubtype = substr($RIFFheader, 8, 4);
     switch ($RIFFtype) {
         case 'FORM':
             // AIFF, AIFC
             $info['fileformat'] = 'aiff';
             $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
             $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, $offset + $thisfile_riff['header_size'] - 4);
             break;
         case 'RIFF':
             // AVI, WAV, etc
         // AVI, WAV, etc
         case 'SDSS':
             // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
         // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
         case 'RMP3':
             // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
             $info['fileformat'] = 'riff';
             $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
             if ($RIFFsubtype == 'RMP3') {
                 // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
                 $RIFFsubtype = 'WAVE';
             }
             $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, $offset + $thisfile_riff['header_size'] - 4);
             if ($info['avdataend'] - $info['filesize'] == 1) {
                 // LiteWave appears to incorrectly *not* pad actual output file
                 // to nearest WORD boundary so may appear to be short by one
                 // byte, in which case - skip warning
                 $info['avdataend'] = $info['filesize'];
             }
             $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size'];
             // 8 = "RIFF" + 32-bit offset
             while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
                 if (!getid3_lib::intValueSupported($nextRIFFoffset + 1024)) {
                     $info['error'][] = 'AVI extends beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
                     $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
                     break;
                 } else {
                     $this->fseek($nextRIFFoffset);
                     $nextRIFFheader = $this->fread(12);
                     if ($nextRIFFoffset == $info['avdataend'] - 1) {
                         if (substr($nextRIFFheader, 0, 1) == "") {
                             // RIFF padded to WORD boundary, we're actually already at the end
                             break;
                         }
                     }
                     $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
                     $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
                     $nextRIFFtype = substr($nextRIFFheader, 8, 4);
                     $chunkdata = array();
                     $chunkdata['offset'] = $nextRIFFoffset + 8;
                     $chunkdata['size'] = $nextRIFFsize;
                     $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
                     switch ($nextRIFFheaderID) {
                         case 'RIFF':
                             $info['avdataend'] = $nextRIFFoffset;
                             if (!getid3_lib::intValueSupported($info['avdataend'])) {
                                 $info['error'][] = 'AVI extends beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
                                 $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
                             }
                             $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
                             if (!isset($thisfile_riff[$nextRIFFtype])) {
                                 $thisfile_riff[$nextRIFFtype] = array();
                             }
                             $thisfile_riff[$nextRIFFtype][] = $chunkdata;
                             break;
                         case 'JUNK':
                             // ignore
                             $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
                             break;
                         case 'IDVX':
                             $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
                             break;
                         default:
                             if ($info['filesize'] == $chunkdata['offset'] - 8 + 128) {
                                 $DIVXTAG = $nextRIFFheader . $this->fread(128 - 12);
                                 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
                                     // 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
                                     $this->warning('Found wrongly-structured DIVXTAG at offset ' . ($this->ftell() - 128) . ', parsing anyway');
                                     $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
                                     break 2;
                                 }
                             }
                             $this->warning('Expecting "RIFF|JUNK|IDVX" at ' . $nextRIFFoffset . ', found "' . $nextRIFFheaderID . '" (' . getid3_lib::PrintHexBytes($nextRIFFheaderID) . ') - skipping rest of file');
                             break 2;
                     }
                 }
             }
             if ($RIFFsubtype == 'WAVE') {
                 $thisfile_riff_WAVE =& $thisfile_riff['WAVE'];
             }
             break;
         default:
             $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "' . $RIFFsubtype . '" instead');
             unset($info['fileformat']);
             return false;
     }
     $streamindex = 0;
     switch ($RIFFsubtype) {
         case 'WAVE':
             if (empty($thisfile_audio['bitrate_mode'])) {
                 $thisfile_audio['bitrate_mode'] = 'cbr';
             }
             if (empty($thisfile_audio_dataformat)) {
                 $thisfile_audio_dataformat = 'wav';
             }
             if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
             }
             if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
                 $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
                 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
                 if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || $thisfile_riff_audio[$streamindex]['bitrate'] == 0) {
                     $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
                     return false;
                 }
                 $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
                 unset($thisfile_riff_audio[$streamindex]['raw']);
                 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
                 $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
                 if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
                     $info['warning'][] = 'Audio codec = ' . $thisfile_audio['codec'];
                 }
                 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
                 $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $thisfile_audio['bitrate']);
                 $thisfile_audio['lossless'] = false;
                 if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
                     switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
                         case 0x1:
                             // PCM
                             $thisfile_audio['lossless'] = true;
                             break;
                         case 0x2000:
                             // AC-3
                             $thisfile_audio_dataformat = 'ac3';
                             break;
                         default:
                             // do nothing
                             break;
                     }
                 }
                 $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
                 $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                 $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
                 $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
             }
             if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
                 // shortcuts
                 $rgadData =& $thisfile_riff_WAVE['rgad'][0]['data'];
                 $thisfile_riff_raw['rgad'] = array('track' => array(), 'album' => array());
                 $thisfile_riff_raw_rgad =& $thisfile_riff_raw['rgad'];
                 $thisfile_riff_raw_rgad_track =& $thisfile_riff_raw_rgad['track'];
                 $thisfile_riff_raw_rgad_album =& $thisfile_riff_raw_rgad['album'];
                 $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
                 $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
                 $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
                 $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
                 $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
                 $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
                 $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
                 $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
                 $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
                 $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
                 $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
                 $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
                 $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
                 $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
                 if ($thisfile_riff_raw_rgad_track['name'] != 0 && $thisfile_riff_raw_rgad_track['originator'] != 0) {
                     $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
                     $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
                     $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
                 }
                 if ($thisfile_riff_raw_rgad_album['name'] != 0 && $thisfile_riff_raw_rgad_album['originator'] != 0) {
                     $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
                     $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
                     $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
                 }
             }
             if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
                 $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
                 // This should be a good way of calculating exact playtime,
                 // but some sample files have had incorrect number of samples,
                 // so cannot use this method
                 // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
                 //     $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
                 // }
             }
             if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
                 $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
             }
             if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_bext_0 =& $thisfile_riff_WAVE['bext'][0];
                 $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
                 $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
                 $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
                 $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
                 $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
                 $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
                 $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
                 $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
                 $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
                 if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
                     if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
                         list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
                         list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
                         $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']);
                     } else {
                         $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
                     }
                 } else {
                     $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
                 }
                 $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
                 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
             }
             if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_MEXT_0 =& $thisfile_riff_WAVE['MEXT'][0];
                 $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
                 $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x1);
                 if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
                     $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x2 ? false : true;
                     $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x4);
                     $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x8);
                     $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
                 }
                 $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
                 $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x1);
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x2);
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x4);
             }
             if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_cart_0 =& $thisfile_riff_WAVE['cart'][0];
                 $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
                 $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
                 $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
                 $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
                 $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
                 $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
                 $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
                 $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
                 $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
                 $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
                 $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
                 $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
                 $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
                 $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
                 $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
                 $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
                 for ($i = 0; $i < 8; $i++) {
                     $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + $i * 8, 4);
                     $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));
                 }
                 $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
                 $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
                 $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
                 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
             }
             if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
                 // SoundMiner metadata
                 // shortcuts
                 $thisfile_riff_WAVE_SNDM_0 =& $thisfile_riff_WAVE['SNDM'][0];
                 $thisfile_riff_WAVE_SNDM_0_data =& $thisfile_riff_WAVE_SNDM_0['data'];
                 $SNDM_startoffset = 0;
                 $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
                 while ($SNDM_startoffset < $SNDM_endoffset) {
                     $SNDM_thisTagOffset = 0;
                     $SNDM_thisTagSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
                     $SNDM_thisTagOffset += 4;
                     $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
                     $SNDM_thisTagOffset += 4;
                     $SNDM_thisTagDataSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
                     $SNDM_thisTagOffset += 2;
                     $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
                     $SNDM_thisTagOffset += 2;
                     $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
                     $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
                     if ($SNDM_thisTagSize != 4 + 4 + 2 + 2 + $SNDM_thisTagDataSize) {
                         $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) . ')';
                         break;
                     } elseif ($SNDM_thisTagSize <= 0) {
                         $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset ' . $SNDM_startoffset . ' (file offset ' . ($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset) . ')';
                         break;
                     }
                     $SNDM_startoffset += $SNDM_thisTagSize;
                     $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
                     if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
                         $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
                     } else {
                         $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "' . $SNDM_thisTagKey . '" at offset ' . $SNDM_startoffset . ' (file offset ' . ($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset) . ')';
                     }
                 }
                 $tagmapping = array('tracktitle' => 'title', 'category' => 'genre', 'cdtitle' => 'album', 'tracktitle' => 'title');
                 foreach ($tagmapping as $fromkey => $tokey) {
                     if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
                         $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
                     }
                 }
             }
             if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
                 // requires functions simplexml_load_string and get_object_vars
                 if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
                     $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
                     if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
                         @(list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']));
                         $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
                     }
                     if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
                         @(list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']));
                         $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
                     }
                     if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
                         $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'] . $parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
                         $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
                         $h = floor($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
                         $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600) / 60);
                         $s = floor($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600 - $m * 60);
                         $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600 - $m * 60 - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
                         $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
                         $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
                     }
                     unset($parsedXML);
                 }
             }
             if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
                 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
                 $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $thisfile_audio['bitrate']);
             }
             if (!empty($info['wavpack'])) {
                 $thisfile_audio_dataformat = 'wavpack';
                 $thisfile_audio['bitrate_mode'] = 'vbr';
                 $thisfile_audio['encoder'] = 'WavPack v' . $info['wavpack']['version'];
                 // Reset to the way it was - RIFF parsing will have messed this up
                 $info['avdataend'] = $Original['avdataend'];
                 $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
                 $this->fseek($info['avdataoffset'] - 44);
                 $RIFFdata = $this->fread(44);
                 $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
                 $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
                 if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
                     $info['avdataend'] -= $OrignalRIFFheaderSize - $OrignalRIFFdataSize;
                     $this->fseek($info['avdataend']);
                     $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
                 }
                 // move the data chunk after all other chunks (if any)
                 // so that the RIFF parser doesn't see EOF when trying
                 // to skip over the data chunk
                 $RIFFdata = substr($RIFFdata, 0, 36) . substr($RIFFdata, 44) . substr($RIFFdata, 36, 8);
                 $getid3_riff = new getid3_riff($this->getid3);
                 $getid3_riff->ParseRIFFdata($RIFFdata);
                 unset($getid3_riff);
             }
             if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
                 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
                     case 0x1:
                         // PCM
                         if (!empty($info['ac3'])) {
                             // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
                             $thisfile_audio['wformattag'] = 0x2000;
                             $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']);
                             $thisfile_audio['lossless'] = false;
                             $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
                             $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
                         }
                         break;
                     case 0x8ae:
                         // ClearJump LiteWave
                         $thisfile_audio['bitrate_mode'] = 'vbr';
                         $thisfile_audio_dataformat = 'litewave';
                         //typedef struct tagSLwFormat {
                         //  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
                         //  DWORD   m_dwScale;         // scale factor for lossy compression
                         //  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
                         //  WORD    m_wQuality;        // alias for the scale factor
                         //  WORD    m_wMarkDistance;   // distance between marks in bytes
                         //  WORD    m_wReserved;
                         //
                         //  //following paramters are ignored if CF_FILESRC is not set
                         //  DWORD   m_dwOrgSize;       // original file size in bytes
                         //  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
                         //  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
                         //
                         //  PCMWAVEFORMAT m_OrgWf;     // original wave format
                         // }SLwFormat, *PSLwFormat;
                         // shortcut
                         $thisfile_riff['litewave']['raw'] = array();
                         $riff_litewave =& $thisfile_riff['litewave'];
                         $riff_litewave_raw =& $riff_litewave['raw'];
                         $flags = array('compression_method' => 1, 'compression_flags' => 1, 'm_dwScale' => 4, 'm_dwBlockSize' => 4, 'm_wQuality' => 2, 'm_wMarkDistance' => 2, 'm_wReserved' => 2, 'm_dwOrgSize' => 4, 'm_bFactExists' => 2, 'm_dwRiffChunkSize' => 4);
                         $litewave_offset = 18;
                         foreach ($flags as $flag => $length) {
                             $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
                             $litewave_offset += $length;
                         }
                         //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
                         $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
                         $riff_litewave['flags']['raw_source'] = $riff_litewave_raw['compression_flags'] & 0x1 ? false : true;
                         $riff_litewave['flags']['vbr_blocksize'] = $riff_litewave_raw['compression_flags'] & 0x2 ? false : true;
                         $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x4);
                         $thisfile_audio['lossless'] = $riff_litewave_raw['m_wQuality'] == 100 ? true : false;
                         $thisfile_audio['encoder_options'] = '-q' . $riff_litewave['quality_factor'];
                         break;
                     default:
                         break;
                 }
             }
             if ($info['avdataend'] > $info['filesize']) {
                 switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
                     case 'wavpack':
                         // WavPack
                     // WavPack
                     case 'lpac':
                         // LPAC
                     // LPAC
                     case 'ofr':
                         // OptimFROG
                     // OptimFROG
                     case 'ofs':
                         // OptimFROG DualStream
                         // lossless compressed audio formats that keep original RIFF headers - skip warning
                         break;
                     case 'litewave':
                         if ($info['avdataend'] - $info['filesize'] == 1) {
                             // LiteWave appears to incorrectly *not* pad actual output file
                             // to nearest WORD boundary so may appear to be short by one
                             // byte, in which case - skip warning
                         } else {
                             // Short by more than one byte, throw warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         }
                         break;
                     default:
                         if ($info['avdataend'] - $info['filesize'] == 1 && $thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2 == 0 && ($info['filesize'] - $info['avdataoffset']) % 2 == 1) {
                             // output file appears to be incorrectly *not* padded to nearest WORD boundary
                             // Output less severe warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         } else {
                             // Short by more than one byte, throw warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         }
                         break;
                 }
             }
             if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
                 if ($info['avdataend'] - $info['avdataoffset'] - $info['mpeg']['audio']['LAME']['audio_bytes'] == 1) {
                     $info['avdataend']--;
                     $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
                 }
             }
             if (isset($thisfile_audio_dataformat) && $thisfile_audio_dataformat == 'ac3') {
                 unset($thisfile_audio['bits_per_sample']);
                 if (!empty($info['ac3']['bitrate']) && $info['ac3']['bitrate'] != $thisfile_audio['bitrate']) {
                     $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
                 }
             }
             break;
         case 'AVI ':
             $thisfile_video['bitrate_mode'] = 'vbr';
             // maybe not, but probably
             $thisfile_video['dataformat'] = 'avi';
             $info['mime_type'] = 'video/avi';
             if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
                 if (isset($thisfile_riff['AVIX'])) {
                     $info['avdataend'] = $thisfile_riff['AVIX'][count($thisfile_riff['AVIX']) - 1]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][count($thisfile_riff['AVIX']) - 1]['chunks']['movi']['size'];
                 } else {
                     $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
                 }
                 if ($info['avdataend'] > $info['filesize']) {
                     $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)';
                     $info['avdataend'] = $info['filesize'];
                 }
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
                 //$bIndexType = array(
                 //	0x00 => 'AVI_INDEX_OF_INDEXES',
                 //	0x01 => 'AVI_INDEX_OF_CHUNKS',
                 //	0x80 => 'AVI_INDEX_IS_DATA',
                 //);
                 //$bIndexSubtype = array(
                 //	0x01 => array(
                 //		0x01 => 'AVI_INDEX_2FIELD',
                 //	),
                 //);
                 foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
                     $ahsisd =& $thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
                     $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2));
                     $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1));
                     $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1));
                     $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4));
                     $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4);
                     $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
                     //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
                     //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
                     unset($ahsisd);
                 }
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
                 $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
                 // shortcut
                 $thisfile_riff_raw['avih'] = array();
                 $thisfile_riff_raw_avih =& $thisfile_riff_raw['avih'];
                 $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4));
                 // frame display rate (or 0L)
                 if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
                     $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
                     return false;
                 }
                 $flags = array('dwMaxBytesPerSec', 'dwPaddingGranularity', 'dwFlags', 'dwTotalFrames', 'dwInitialFrames', 'dwStreams', 'dwSuggestedBufferSize', 'dwWidth', 'dwHeight', 'dwScale', 'dwRate', 'dwStart', 'dwLength');
                 $avih_offset = 4;
                 foreach ($flags as $flag) {
                     $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
                     $avih_offset += 4;
                 }
                 $flags = array('hasindex' => 0x10, 'mustuseindex' => 0x20, 'interleaved' => 0x100, 'trustcktype' => 0x800, 'capturedfile' => 0x10000, 'copyrighted' => 0x20010);
                 foreach ($flags as $flag => $value) {
                     $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
                 }
                 // shortcut
                 $thisfile_riff_video[$streamindex] = array();
                 $thisfile_riff_video_current =& $thisfile_riff_video[$streamindex];
                 if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
                     $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
                     $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
                 }
                 if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
                     $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
                     $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
                 }
                 if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
                     $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
                     $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
                 }
                 $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
                 $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
                 if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
                     for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
                         if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
                             $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
                             $strhfccType = substr($strhData, 0, 4);
                             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
                                 $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
                                 // shortcut
                                 $thisfile_riff_raw_strf_strhfccType_streamindex =& $thisfile_riff_raw['strf'][$strhfccType][$streamindex];
                                 switch ($strhfccType) {
                                     case 'auds':
                                         $thisfile_audio['bitrate_mode'] = 'cbr';
                                         $thisfile_audio_dataformat = 'wav';
                                         if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
                                             $streamindex = count($thisfile_riff_audio);
                                         }
                                         $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
                                         // shortcut
                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
                                         $thisfile_audio_streams_currentstream =& $thisfile_audio['streams'][$streamindex];
                                         if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
                                             unset($thisfile_audio_streams_currentstream['bits_per_sample']);
                                         }
                                         $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
                                         unset($thisfile_audio_streams_currentstream['raw']);
                                         // shortcut
                                         $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
                                         unset($thisfile_riff_audio[$streamindex]['raw']);
                                         $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
                                         $thisfile_audio['lossless'] = false;
                                         switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
                                             case 0x1:
                                                 // PCM
                                                 $thisfile_audio_dataformat = 'wav';
                                                 $thisfile_audio['lossless'] = true;
                                                 break;
                                             case 0x50:
                                                 // MPEG Layer 2 or Layer 1
                                                 $thisfile_audio_dataformat = 'mp2';
                                                 // Assume Layer-2
                                                 break;
                                             case 0x55:
                                                 // MPEG Layer 3
                                                 $thisfile_audio_dataformat = 'mp3';
                                                 break;
                                             case 0xff:
                                                 // AAC
                                                 $thisfile_audio_dataformat = 'aac';
                                                 break;
                                             case 0x161:
                                                 // Windows Media v7 / v8 / v9
                                             // Windows Media v7 / v8 / v9
                                             case 0x162:
                                                 // Windows Media Professional v9
                                             // Windows Media Professional v9
                                             case 0x163:
                                                 // Windows Media Lossess v9
                                                 $thisfile_audio_dataformat = 'wma';
                                                 break;
                                             case 0x2000:
                                                 // AC-3
                                                 $thisfile_audio_dataformat = 'ac3';
                                                 break;
                                             case 0x2001:
                                                 // DTS
                                                 $thisfile_audio_dataformat = 'dts';
                                                 break;
                                             default:
                                                 $thisfile_audio_dataformat = 'wav';
                                                 break;
                                         }
                                         $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
                                         $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
                                         $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                                         break;
                                     case 'iavs':
                                     case 'vids':
                                         // shortcut
                                         $thisfile_riff_raw['strh'][$i] = array();
                                         $thisfile_riff_raw_strh_current =& $thisfile_riff_raw['strh'][$i];
                                         $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4);
                                         // same as $strhfccType;
                                         $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
                                         $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4));
                                         // Contains AVITF_* flags
                                         $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
                                         $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
                                         $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
                                         $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
                                         $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
                                         $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
                                         $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
                                         $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
                                         $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
                                         $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
                                         $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
                                         $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
                                         $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
                                         if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
                                             $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
                                             $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
                                         }
                                         $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
                                         $thisfile_video['pixel_aspect_ratio'] = (double) 1;
                                         switch ($thisfile_riff_raw_strh_current['fccHandler']) {
                                             case 'HFYU':
                                                 // Huffman Lossless Codec
                                             // Huffman Lossless Codec
                                             case 'IRAW':
                                                 // Intel YUV Uncompressed
                                             // Intel YUV Uncompressed
                                             case 'YUY2':
                                                 // Uncompressed YUV 4:2:2
                                                 $thisfile_video['lossless'] = true;
                                                 break;
                                             default:
                                                 $thisfile_video['lossless'] = false;
                                                 break;
                                         }
                                         switch ($strhfccType) {
                                             case 'vids':
                                                 $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), $info['fileformat'] == 'riff');
                                                 $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
                                                 if ($thisfile_riff_video_current['codec'] == 'DV') {
                                                     $thisfile_riff_video_current['dv_type'] = 2;
                                                 }
                                                 break;
                                             case 'iavs':
                                                 $thisfile_riff_video_current['dv_type'] = 1;
                                                 break;
                                         }
                                         break;
                                     default:
                                         $info['warning'][] = 'Unhandled fccType for stream (' . $i . '): "' . $strhfccType . '"';
                                         break;
                                 }
                             }
                         }
                         if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
                             $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
                             if (self::fourccLookup($thisfile_video['fourcc'])) {
                                 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
                                 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
                             }
                             switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
                                 case 'HFYU':
                                     // Huffman Lossless Codec
                                 // Huffman Lossless Codec
                                 case 'IRAW':
                                     // Intel YUV Uncompressed
                                 // Intel YUV Uncompressed
                                 case 'YUY2':
                                     // Uncompressed YUV 4:2:2
                                     $thisfile_video['lossless'] = true;
                                     //$thisfile_video['bits_per_sample'] = 24;
                                     break;
                                 default:
                                     $thisfile_video['lossless'] = false;
                                     //$thisfile_video['bits_per_sample'] = 24;
                                     break;
                             }
                         }
                     }
                 }
             }
             break;
         case 'CDDA':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = 'cda';
             $thisfile_audio['lossless'] = true;
             unset($info['mime_type']);
             $info['avdataoffset'] = 44;
             if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
                 // shortcut
                 $thisfile_riff_CDDA_fmt_0 =& $thisfile_riff['CDDA']['fmt '][0];
                 $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
                 $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
                 $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
                 $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
                 $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
                 $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
                 $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
                 $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (double) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
                 $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (double) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
                 $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
                 $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
                 // hardcoded data for CD-audio
                 $thisfile_audio['sample_rate'] = 44100;
                 $thisfile_audio['channels'] = 2;
                 $thisfile_audio['bits_per_sample'] = 16;
                 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
                 $thisfile_audio['bitrate_mode'] = 'cbr';
             }
             break;
         case 'AIFF':
         case 'AIFC':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = 'aiff';
             $thisfile_audio['lossless'] = true;
             $info['mime_type'] = 'audio/x-aiff';
             if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
                 if ($info['avdataend'] > $info['filesize']) {
                     if ($info['avdataend'] == $info['filesize'] + 1 && $info['filesize'] % 2 == 1) {
                         // structures rounded to 2-byte boundary, but dumb encoders
                         // forget to pad end of file to make this actually work
                     } else {
                         $info['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'] . ' bytes of audio data, only ' . ($info['filesize'] - $info['avdataoffset']) . ' bytes found';
                     }
                     $info['avdataend'] = $info['filesize'];
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_RIFFsubtype_COMM_0_data =& $thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
                 $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
                 $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
                 $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
                 $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
                 if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
                     $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
                     $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
                     $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
                     switch ($thisfile_riff_audio['codec_name']) {
                         case 'NONE':
                             $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
                             $thisfile_audio['lossless'] = true;
                             break;
                         case '':
                             switch ($thisfile_riff_audio['codec_fourcc']) {
                                 // http://developer.apple.com/qa/snd/snd07.html
                                 case 'sowt':
                                     $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
                                     $thisfile_audio['lossless'] = true;
                                     break;
                                 case 'twos':
                                     $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
                                     $thisfile_audio['lossless'] = true;
                                     break;
                                 default:
                                     break;
                             }
                             break;
                         default:
                             $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
                             $thisfile_audio['lossless'] = false;
                             break;
                     }
                 }
                 $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
                 if ($thisfile_riff_audio['bits_per_sample'] > 0) {
                     $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
                 }
                 $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
                 if ($thisfile_audio['sample_rate'] == 0) {
                     $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
                     return false;
                 }
                 $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
             }
             if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
                 $offset = 0;
                 $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
                 $offset += 2;
                 for ($i = 0; $i < $CommentCount; $i++) {
                     $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
                     $offset += 4;
                     $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
                     $offset += 2;
                     $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
                     $offset += 2;
                     $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
                     $offset += $CommentLength;
                     $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
                     $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
                 }
             }
             $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment');
             foreach ($CommentsChunkNames as $key => $value) {
                 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
                     $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
                 }
             }
             /*
             				if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
             					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
             					$getid3_temp = new getID3();
             					$getid3_temp->openfile($this->getid3->filename);
             					$getid3_id3v2 = new getid3_id3v2($getid3_temp);
             					$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
             					if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
             						$info['id3v2'] = $getid3_temp->info['id3v2'];
             					}
             					unset($getid3_temp, $getid3_id3v2);
             				}
             */
             break;
         case '8SVX':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = '8svx';
             $thisfile_audio['bits_per_sample'] = 8;
             $thisfile_audio['channels'] = 1;
             // overridden below, if need be
             $info['mime_type'] = 'audio/x-aiff';
             if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
                 if ($info['avdataend'] > $info['filesize']) {
                     $info['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'] . ' bytes of audio data, only ' . ($info['filesize'] - $info['avdataoffset']) . ' bytes found';
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
                 // shortcut
                 $thisfile_riff_RIFFsubtype_VHDR_0 =& $thisfile_riff[$RIFFsubtype]['VHDR'][0];
                 $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
                 $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
                 $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
                 $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
                 $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
                 switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
                     case 0:
                         $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
                         $thisfile_audio['lossless'] = true;
                         $ActualBitsPerSample = 8;
                         break;
                     case 1:
                         $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
                         $thisfile_audio['lossless'] = false;
                         $ActualBitsPerSample = 4;
                         break;
                     default:
                         $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "' . sCompression . '"';
                         break;
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
                 $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
                 switch ($ChannelsIndex) {
                     case 6:
                         // Stereo
                         $thisfile_audio['channels'] = 2;
                         break;
                     case 2:
                         // Left channel only
                     // Left channel only
                     case 4:
                         // Right channel only
                         $thisfile_audio['channels'] = 1;
                         break;
                     default:
                         $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "' . $ChannelsIndex . '"';
                         break;
                 }
             }
             $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment');
             foreach ($CommentsChunkNames as $key => $value) {
                 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
                     $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
                 }
             }
             $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
             if (!empty($thisfile_audio['bitrate'])) {
                 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
             }
             break;
         case 'CDXA':
             $info['mime_type'] = 'video/mpeg';
             if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
                 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.mpeg.php', __FILE__, false)) {
                     $getid3_temp = new getID3();
                     $getid3_temp->openfile($this->getid3->filename);
                     $getid3_mpeg = new getid3_mpeg($getid3_temp);
                     $getid3_mpeg->Analyze();
                     if (empty($getid3_temp->info['error'])) {
                         $info['audio'] = $getid3_temp->info['audio'];
                         $info['video'] = $getid3_temp->info['video'];
                         $info['mpeg'] = $getid3_temp->info['mpeg'];
                         $info['warning'] = $getid3_temp->info['warning'];
                     }
                     unset($getid3_temp, $getid3_mpeg);
                 }
             }
             break;
         default:
             $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "' . $RIFFsubtype . '" instead';
             unset($info['fileformat']);
             break;
     }
     switch ($RIFFsubtype) {
         case 'WAVE':
         case 'AIFF':
         case 'AIFC':
             $ID3v2_key_good = 'id3 ';
             $ID3v2_keys_bad = array('ID3 ', 'tag ');
             foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
                 if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
                     $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
                     $info['warning'][] = 'mapping "' . $ID3v2_key_bad . '" chunk to "' . $ID3v2_key_good . '"';
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.tag.id3v2.php', __FILE__, true);
                 $getid3_temp = new getID3();
                 $getid3_temp->openfile($this->getid3->filename);
                 $getid3_id3v2 = new getid3_id3v2($getid3_temp);
                 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
                 if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
                     $info['id3v2'] = $getid3_temp->info['id3v2'];
                 }
                 unset($getid3_temp, $getid3_id3v2);
             }
             break;
     }
     if (isset($thisfile_riff_raw['fmt ']['wFormatTag']) && $thisfile_riff_raw['fmt ']['wFormatTag'] == 1) {
         // http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html
         $this->fseek($info['avdataoffset']);
         $FirstFourBytes = $this->fread(4);
         if (preg_match('/^\\xFF\\x1F\\x00\\xE8/s', $FirstFourBytes)) {
             // DTSWAV
             $thisfile_audio_dataformat = 'dts';
         } elseif (preg_match('/^\\x7F\\xFF\\x80\\x01/s', $FirstFourBytes)) {
             // DTS, but this probably shouldn't happen
             $thisfile_audio_dataformat = 'dts';
         }
     }
     if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
         $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
     }
     if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
         self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
     }
     if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
         self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
     }
     if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
         $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
     }
     if (!isset($info['playtime_seconds'])) {
         $info['playtime_seconds'] = 0;
     }
     if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
         // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
         $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
     } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
         $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
     }
     if ($info['playtime_seconds'] > 0) {
         if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
             if (!isset($info['bitrate'])) {
                 $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
             if (!isset($thisfile_audio['bitrate'])) {
                 $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
             if (!isset($thisfile_video['bitrate'])) {
                 $thisfile_video['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         }
     }
     if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && $thisfile_audio['bitrate'] > 0 && $info['playtime_seconds'] > 0) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
         $thisfile_audio['bitrate'] = 0;
         $thisfile_video['bitrate'] = $info['bitrate'];
         foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
             $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
             $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
         }
         if ($thisfile_video['bitrate'] <= 0) {
             unset($thisfile_video['bitrate']);
         }
         if ($thisfile_audio['bitrate'] <= 0) {
             unset($thisfile_audio['bitrate']);
         }
     }
     if (isset($info['mpeg']['audio'])) {
         $thisfile_audio_dataformat = 'mp' . $info['mpeg']['audio']['layer'];
         $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
         $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
         $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
         $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
         if (!empty($info['mpeg']['audio']['codec'])) {
             $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'] . ' ' . $thisfile_audio['codec'];
         }
         if (!empty($thisfile_audio['streams'])) {
             foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
                 if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
                     $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
                     $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
                     $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
                     $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                     $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
                 }
             }
         }
         $getid3_mp3 = new getid3_mp3($this->getid3);
         $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
         unset($getid3_mp3);
     }
     if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && $thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0) {
         switch ($thisfile_audio_dataformat) {
             case 'ac3':
                 // ignore bits_per_sample
                 break;
             default:
                 $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
                 break;
         }
     }
     if (empty($thisfile_riff_raw)) {
         unset($thisfile_riff['raw']);
     }
     if (empty($thisfile_riff_audio)) {
         unset($thisfile_riff['audio']);
     }
     if (empty($thisfile_riff_video)) {
         unset($thisfile_riff['video']);
     }
     return true;
 }
 static function hash_data($file, $offset, $end, $algorithm)
 {
     if (!getid3_lib::intValueSupported($end)) {
         return false;
     }
     switch ($algorithm) {
         case 'md5':
             $hash_function = 'md5_file';
             $unix_call = 'md5sum';
             $windows_call = 'md5sum.exe';
             $hash_length = 32;
             break;
         case 'sha1':
             $hash_function = 'sha1_file';
             $unix_call = 'sha1sum';
             $windows_call = 'sha1sum.exe';
             $hash_length = 40;
             break;
         default:
             throw new Exception('Invalid algorithm (' . $algorithm . ') in getid3_lib::hash_data()');
             break;
     }
     $size = $end - $offset;
     while (true) {
         if (GETID3_OS_ISWINDOWS) {
             // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
             // Fall back to create-temp-file method:
             if ($algorithm == 'sha1') {
                 break;
             }
             $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
             foreach ($RequiredFiles as $required_file) {
                 if (!is_readable(GETID3_HELPERAPPSDIR . $required_file)) {
                     // helper apps not available - fall back to old method
                     break;
                 }
             }
             $commandline = GETID3_HELPERAPPSDIR . 'head.exe -c ' . $end . ' "' . escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)) . '" | ';
             $commandline .= GETID3_HELPERAPPSDIR . 'tail.exe -c ' . $size . ' | ';
             $commandline .= GETID3_HELPERAPPSDIR . $windows_call;
         } else {
             $commandline = 'head -c' . $end . ' ' . escapeshellarg($file) . ' | ';
             $commandline .= 'tail -c' . $size . ' | ';
             $commandline .= $unix_call;
         }
         if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
             $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call ' . $algorithm . ' algorithm';
             break;
         }
         return substr(`{$commandline}`, 0, $hash_length);
     }
     // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
     if (($data_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) {
         // can't find anywhere to create a temp file, just die
         return false;
     }
     // Init
     $result = false;
     // copy parts of file
     ob_start();
     if ($fp = fopen($file, 'rb')) {
         ob_end_clean();
         ob_start();
         if ($fp_data = fopen($data_filename, 'wb')) {
             fseek($fp, $offset, SEEK_SET);
             $byteslefttowrite = $end - $offset;
             while ($byteslefttowrite > 0 && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) {
                 $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite);
                 $byteslefttowrite -= $byteswritten;
             }
             fclose($fp_data);
             $result = $hash_function($data_filename);
         } else {
             $errormessage = ob_get_contents();
             ob_end_clean();
         }
         fclose($fp);
     } else {
         $errormessage = ob_get_contents();
         ob_end_clean();
     }
     unlink($data_filename);
     return $result;
 }
Пример #16
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'zip';
     $info['zip']['encoding'] = 'ISO-8859-1';
     $info['zip']['files'] = array();
     $info['zip']['compressed_size'] = 0;
     $info['zip']['uncompressed_size'] = 0;
     $info['zip']['entries_count'] = 0;
     if (!getid3_lib::intValueSupported($info['filesize'])) {
         $info['error'][] = 'File is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB, not supported by PHP';
         return false;
     } else {
         $EOCDsearchData = '';
         $EOCDsearchCounter = 0;
         while ($EOCDsearchCounter++ < 512) {
             $this->fseek(-128 * $EOCDsearchCounter, SEEK_END);
             $EOCDsearchData = $this->fread(128) . $EOCDsearchData;
             if (strstr($EOCDsearchData, 'PK' . "")) {
                 $EOCDposition = strpos($EOCDsearchData, 'PK' . "");
                 $this->fseek(-128 * $EOCDsearchCounter + $EOCDposition, SEEK_END);
                 $info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory();
                 $this->fseek($info['zip']['end_central_directory']['directory_offset']);
                 $info['zip']['entries_count'] = 0;
                 while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
                     $info['zip']['central_directory'][] = $centraldirectoryentry;
                     $info['zip']['entries_count']++;
                     $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
                     $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
                     //if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid
                     if (!empty($centraldirectoryentry['filename'])) {
                         $info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
                     }
                 }
                 if ($info['zip']['entries_count'] == 0) {
                     $info['error'][] = 'No Central Directory entries found (truncated file?)';
                     return false;
                 }
                 if (!empty($info['zip']['end_central_directory']['comment'])) {
                     $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
                 }
                 if (isset($info['zip']['central_directory'][0]['compression_method'])) {
                     $info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method'];
                 }
                 if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) {
                     $info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed'];
                 }
                 if (isset($info['zip']['compression_method']) && $info['zip']['compression_method'] == 'store' && !isset($info['zip']['compression_speed'])) {
                     $info['zip']['compression_speed'] = 'store';
                 }
                 // secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each
                 // Local File Header entry will
                 foreach ($info['zip']['central_directory'] as $central_directory_entry) {
                     $this->fseek($central_directory_entry['entry_offset']);
                     if ($fileentry = $this->ZIPparseLocalFileHeader()) {
                         $info['zip']['entries'][] = $fileentry;
                     } else {
                         $info['warning'][] = 'Error parsing Local File Header at offset ' . $central_directory_entry['entry_offset'];
                     }
                 }
                 if (!empty($info['zip']['files']['[Content_Types].xml']) && !empty($info['zip']['files']['_rels']['.rels']) && !empty($info['zip']['files']['docProps']['app.xml']) && !empty($info['zip']['files']['docProps']['core.xml'])) {
                     // http://technet.microsoft.com/en-us/library/cc179224.aspx
                     $info['fileformat'] = 'zip.msoffice';
                     if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
                         $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
                     } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
                         $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                     } elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
                         $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
                     }
                 }
                 return true;
             }
         }
     }
     if (!$this->getZIPentriesFilepointer()) {
         unset($info['zip']);
         $info['fileformat'] = '';
         $info['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
         return false;
     }
     // central directory couldn't be found and/or parsed
     // scan through actual file data entries, recover as much as possible from probable trucated file
     if ($info['zip']['compressed_size'] > $info['filesize'] - 46 - 22) {
         $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes (' . $info['zip']['compressed_size'] . ' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures (' . ($info['filesize'] - 46 - 22) . ' bytes)';
     }
     $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
     foreach ($info['zip']['entries'] as $key => $valuearray) {
         $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
     }
     return true;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'zip';
     $info['zip']['encoding'] = 'ISO-8859-1';
     $info['zip']['files'] = array();
     $info['zip']['compressed_size'] = 0;
     $info['zip']['uncompressed_size'] = 0;
     $info['zip']['entries_count'] = 0;
     if (!getid3_lib::intValueSupported($info['filesize'])) {
         $info['error'][] = 'File is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB, not supported by PHP';
         return false;
     } else {
         $EOCDsearchData = '';
         $EOCDsearchCounter = 0;
         while ($EOCDsearchCounter++ < 512) {
             fseek($this->getid3->fp, -128 * $EOCDsearchCounter, SEEK_END);
             $EOCDsearchData = fread($this->getid3->fp, 128) . $EOCDsearchData;
             if (strstr($EOCDsearchData, 'PK' . "")) {
                 $EOCDposition = strpos($EOCDsearchData, 'PK' . "");
                 fseek($this->getid3->fp, -128 * $EOCDsearchCounter + $EOCDposition, SEEK_END);
                 $info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory();
                 fseek($this->getid3->fp, $info['zip']['end_central_directory']['directory_offset'], SEEK_SET);
                 $info['zip']['entries_count'] = 0;
                 while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
                     $info['zip']['central_directory'][] = $centraldirectoryentry;
                     $info['zip']['entries_count']++;
                     $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
                     $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
                     if ($centraldirectoryentry['uncompressed_size'] > 0) {
                         $info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
                     }
                 }
                 if ($info['zip']['entries_count'] == 0) {
                     $info['error'][] = 'No Central Directory entries found (truncated file?)';
                     return false;
                 }
                 if (!empty($info['zip']['end_central_directory']['comment'])) {
                     $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment'];
                 }
                 if (isset($info['zip']['central_directory'][0]['compression_method'])) {
                     $info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method'];
                 }
                 if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) {
                     $info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed'];
                 }
                 if (isset($info['zip']['compression_method']) && $info['zip']['compression_method'] == 'store' && !isset($info['zip']['compression_speed'])) {
                     $info['zip']['compression_speed'] = 'store';
                 }
                 return true;
             }
         }
     }
     if ($this->getZIPentriesFilepointer()) {
         // central directory couldn't be found and/or parsed
         // scan through actual file data entries, recover as much as possible from probable trucated file
         if ($info['zip']['compressed_size'] > $info['filesize'] - 46 - 22) {
             $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes (' . $info['zip']['compressed_size'] . ' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures (' . ($info['filesize'] - 46 - 22) . ' bytes)';
         }
         $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
         foreach ($info['zip']['entries'] as $key => $valuearray) {
             $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
         }
         return true;
     } else {
         unset($info['zip']);
         $info['fileformat'] = '';
         $info['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
         return false;
     }
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     if (!getid3_lib::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for APEtags because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $id3v1tagsize = 128;
     $apetagheadersize = 32;
     $lyrics3tagsize = 10;
     if ($this->overrideendoffset == 0) {
         fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
         $APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
         //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
         if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
             // APE tag found before ID3v1
             $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
             //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
         } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
             // APE tag found, no ID3v1
             $info['ape']['tag_offset_end'] = $info['filesize'];
         }
     } else {
         fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET);
         if (fread($this->getid3->fp, 8) == 'APETAGEX') {
             $info['ape']['tag_offset_end'] = $this->overrideendoffset;
         }
     }
     if (!isset($info['ape']['tag_offset_end'])) {
         // APE tag not found
         unset($info['ape']);
         return false;
     }
     // shortcut
     $thisfile_ape =& $info['ape'];
     fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
     $APEfooterData = fread($this->getid3->fp, 32);
     if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
         $info['error'][] = 'Error parsing APE footer at offset ' . $thisfile_ape['tag_offset_end'];
         return false;
     }
     if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
         fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
         $thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp);
         $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
     } else {
         $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
         fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET);
         $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']);
     }
     $info['avdataend'] = $thisfile_ape['tag_offset_start'];
     if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end']) {
         $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
         unset($info['id3v1']);
         foreach ($info['warning'] as $key => $value) {
             if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
                 unset($info['warning'][$key]);
                 sort($info['warning']);
                 break;
             }
         }
     }
     $offset = 0;
     if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
         if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
             $offset += $apetagheadersize;
         } else {
             $info['error'][] = 'Error parsing APE header at offset ' . $thisfile_ape['tag_offset_start'];
             return false;
         }
     }
     // shortcut
     $info['replay_gain'] = array();
     $thisfile_replaygain =& $info['replay_gain'];
     for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
         $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
         $offset += 4;
         $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
         $offset += 4;
         if (strstr(substr($APEtagData, $offset), "") === false) {
             $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ($thisfile_ape['tag_offset_start'] + $offset);
             return false;
         }
         $ItemKeyLength = strpos($APEtagData, "", $offset) - $offset;
         $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
         // shortcut
         $thisfile_ape['items'][$item_key] = array();
         $thisfile_ape_items_current =& $thisfile_ape['items'][$item_key];
         $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
         $offset += $ItemKeyLength + 1;
         // skip 0x00 terminator
         $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
         $offset += $value_size;
         $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
         switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
             case 0:
                 // UTF-8
             // UTF-8
             case 3:
                 // Locator (URL, filename, etc), UTF-8 encoded
                 $thisfile_ape_items_current['data'] = explode("", trim($thisfile_ape_items_current['data']));
                 break;
             default:
                 // binary data
                 break;
         }
         switch (strtolower($item_key)) {
             case 'replaygain_track_gain':
                 $thisfile_replaygain['track']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                 // float casting will see "0,95" as zero!
                 $thisfile_replaygain['track']['originator'] = 'unspecified';
                 break;
             case 'replaygain_track_peak':
                 $thisfile_replaygain['track']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                 // float casting will see "0,95" as zero!
                 $thisfile_replaygain['track']['originator'] = 'unspecified';
                 if ($thisfile_replaygain['track']['peak'] <= 0) {
                     $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: ' . $thisfile_replaygain['track']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")';
                 }
                 break;
             case 'replaygain_album_gain':
                 $thisfile_replaygain['album']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                 // float casting will see "0,95" as zero!
                 $thisfile_replaygain['album']['originator'] = 'unspecified';
                 break;
             case 'replaygain_album_peak':
                 $thisfile_replaygain['album']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                 // float casting will see "0,95" as zero!
                 $thisfile_replaygain['album']['originator'] = 'unspecified';
                 if ($thisfile_replaygain['album']['peak'] <= 0) {
                     $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: ' . $thisfile_replaygain['album']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")';
                 }
                 break;
             case 'mp3gain_undo':
                 list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
                 $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
                 $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
                 $thisfile_replaygain['mp3gain']['undo_wrap'] = $mp3gain_undo_wrap == 'Y' ? true : false;
                 break;
             case 'mp3gain_minmax':
                 list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
                 $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
                 $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
                 break;
             case 'mp3gain_album_minmax':
                 list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
                 $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
                 $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
                 break;
             case 'tracknumber':
                 if (is_array($thisfile_ape_items_current['data'])) {
                     foreach ($thisfile_ape_items_current['data'] as $comment) {
                         $thisfile_ape['comments']['track'][] = $comment;
                     }
                 }
                 break;
             case 'cover art (artist)':
             case 'cover art (back)':
             case 'cover art (band logo)':
             case 'cover art (band)':
             case 'cover art (colored fish)':
             case 'cover art (composer)':
             case 'cover art (conductor)':
             case 'cover art (front)':
             case 'cover art (icon)':
             case 'cover art (illustration)':
             case 'cover art (lead)':
             case 'cover art (leaflet)':
             case 'cover art (lyricist)':
             case 'cover art (media)':
             case 'cover art (movie scene)':
             case 'cover art (other icon)':
             case 'cover art (other)':
             case 'cover art (performance)':
             case 'cover art (publisher logo)':
             case 'cover art (recording)':
             case 'cover art (studio)':
                 // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
                 list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("", $thisfile_ape_items_current['data'], 2);
                 $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename'] . "");
                 $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
                 $thisfile_ape_items_current['image_mime'] = '';
                 $imageinfo = array();
                 $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
                 $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
                 do {
                     if ($this->inline_attachments === false) {
                         // skip entirely
                         unset($thisfile_ape_items_current['data']);
                         break;
                     }
                     if ($this->inline_attachments === true) {
                         // great
                     } elseif (is_int($this->inline_attachments)) {
                         if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
                             // too big, skip
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' is too large to process inline (' . number_format($thisfile_ape_items_current['data_length']) . ' bytes)';
                             unset($thisfile_ape_items_current['data']);
                             break;
                         }
                     } elseif (is_string($this->inline_attachments)) {
                         $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
                         if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
                             // cannot write, skip
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)';
                             unset($thisfile_ape_items_current['data']);
                             break;
                         }
                     }
                     // if we get this far, must be OK
                     if (is_string($this->inline_attachments)) {
                         $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $thisfile_ape_items_current['data_offset'];
                         if (!file_exists($destination_filename) || is_writable($destination_filename)) {
                             file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
                         } else {
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)';
                         }
                         $thisfile_ape_items_current['data_filename'] = $destination_filename;
                         unset($thisfile_ape_items_current['data']);
                     } else {
                         if (!isset($info['ape']['comments']['picture'])) {
                             $info['ape']['comments']['picture'] = array();
                         }
                         $info['ape']['comments']['picture'][] = array('data' => $thisfile_ape_items_current['data'], 'image_mime' => $thisfile_ape_items_current['image_mime']);
                     }
                 } while (false);
                 break;
             default:
                 if (is_array($thisfile_ape_items_current['data'])) {
                     foreach ($thisfile_ape_items_current['data'] as $comment) {
                         $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
                     }
                 }
                 break;
         }
     }
     if (empty($thisfile_replaygain)) {
         unset($info['replay_gain']);
     }
     return true;
 }
Пример #19
0
 protected function fseek($bytes, $whence = SEEK_SET)
 {
     if ($this->data_string_flag) {
         switch ($whence) {
             case SEEK_SET:
                 $this->data_string_position = $bytes;
                 break;
             case SEEK_CUR:
                 $this->data_string_position += $bytes;
                 break;
             case SEEK_END:
                 $this->data_string_position = $this->data_string_length + $bytes;
                 break;
         }
         return 0;
     } else {
         $pos = $bytes;
         if ($whence == SEEK_CUR) {
             $pos = $this->ftell() + $bytes;
         } elseif ($whence == SEEK_END) {
             $pos = $this->getid3->info['filesize'] + $bytes;
         }
         if (!getid3_lib::intValueSupported($pos)) {
             throw new getid3_exception('cannot fseek(' . $pos . ') because beyond PHP filesystem limit', 10);
         }
     }
     return fseek($this->getid3->fp, $bytes, $whence);
 }
Пример #20
0
 protected function fread($bytes)
 {
     if ($this->data_string_flag) {
         $this->data_string_position += $bytes;
         return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
     }
     $pos = $this->ftell() + $bytes;
     if (!getid3_lib::intValueSupported($pos)) {
         throw new getid3_exception('cannot fread(' . $bytes . ' from ' . $this->ftell() . ') because beyond PHP filesystem limit', 10);
     }
     return fread($this->getid3->fp, $bytes);
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'ogg';
     // Warn about illegal tags - only vorbiscomments are allowed
     if (isset($info['id3v2'])) {
         $info['warning'][] = 'Illegal ID3v2 tag present.';
     }
     if (isset($info['id3v1'])) {
         $info['warning'][] = 'Illegal ID3v1 tag present.';
     }
     if (isset($info['ape'])) {
         $info['warning'][] = 'Illegal APE tag present.';
     }
     // Page 1 - Stream Header
     $this->fseek($info['avdataoffset']);
     $oggpageinfo = $this->ParseOggPageHeader();
     $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
         $info['error'][] = 'Could not find start of Ogg page in the first ' . $this->getid3->fread_buffer_size() . ' bytes (this might not be an Ogg-Vorbis file?)';
         unset($info['fileformat']);
         unset($info['ogg']);
         return false;
     }
     $filedata = $this->fread($oggpageinfo['page_length']);
     $filedataoffset = 0;
     if (substr($filedata, 0, 4) == 'fLaC') {
         $info['audio']['dataformat'] = 'flac';
         $info['audio']['bitrate_mode'] = 'vbr';
         $info['audio']['lossless'] = true;
     } elseif (substr($filedata, 1, 6) == 'vorbis') {
         $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
     } elseif (substr($filedata, 0, 8) == 'Speex   ') {
         // http://www.speex.org/manual/node10.html
         $info['audio']['dataformat'] = 'speex';
         $info['mime_type'] = 'audio/speex';
         $info['audio']['bitrate_mode'] = 'abr';
         $info['audio']['lossless'] = false;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8);
         // hard-coded to 'Speex   '
         $filedataoffset += 8;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
         $filedataoffset += 20;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
         $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
         $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
         $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
         $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
         $info['audio']['sample_rate'] = $info['speex']['sample_rate'];
         $info['audio']['channels'] = $info['speex']['channels'];
         if ($info['speex']['vbr']) {
             $info['audio']['bitrate_mode'] = 'vbr';
         }
     } elseif (substr($filedata, 0, 7) == "€" . 'theora') {
         // http://www.theora.org/doc/Theora.pdf (section 6.2)
         $info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7);
         // hard-coded to "\x80.'theora'
         $filedataoffset += 7;
         $info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xfc00) >> 10;
         $info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x3e0) >> 5;
         $info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x18) >> 3;
         $info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x7) >> 0;
         // should be 0
         $info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
         $info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
         $info['video']['dataformat'] = 'theora';
         $info['mime_type'] = 'video/ogg';
         //$info['audio']['bitrate_mode'] = 'abr';
         //$info['audio']['lossless']     = false;
         $info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
         $info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
         if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
             $info['video']['frame_rate'] = (double) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
         }
         if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
             $info['video']['pixel_aspect_ratio'] = (double) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
         }
         $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 [' . $this->getid3->version() . '] -- bitrate, playtime and all audio data are currently unavailable';
     } elseif (substr($filedata, 0, 8) == "fishead") {
         // Ogg Skeleton version 3.0 Format Specification
         // http://xiph.org/ogg/doc/skeleton.html
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
         $filedataoffset += 20;
         $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'] . '.' . $info['ogg']['skeleton']['fishead']['raw']['version_minor'];
         $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'];
         $info['ogg']['skeleton']['fishead']['basetime'] = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'];
         $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc'];
         $counter = 0;
         do {
             $oggpageinfo = $this->ParseOggPageHeader();
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno'] . '.' . $counter++] = $oggpageinfo;
             $filedata = $this->fread($oggpageinfo['page_length']);
             $this->fseek($oggpageinfo['page_end_offset']);
             if (substr($filedata, 0, 8) == "fisbone") {
                 $filedataoffset = 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
                 $filedataoffset += 1;
                 $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3);
                 $filedataoffset += 3;
             } elseif (substr($filedata, 1, 6) == 'theora') {
                 $info['video']['dataformat'] = 'theora1';
                 $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 [' . $this->getid3->version() . ']';
                 //break;
             } elseif (substr($filedata, 1, 6) == 'vorbis') {
                 $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
             } else {
                 $info['error'][] = 'unexpected';
                 //break;
             }
             //} while ($oggpageinfo['page_seqno'] == 0);
         } while ($oggpageinfo['page_seqno'] == 0 && substr($filedata, 0, 8) != "fisbone");
         $this->fseek($oggpageinfo['page_start_offset']);
         $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 [' . $this->getid3->version() . ']';
         //return false;
     } else {
         $info['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found "' . substr($filedata, 0, 8) . '"';
         unset($info['ogg']);
         unset($info['mime_type']);
         return false;
     }
     // Page 2 - Comment Header
     $oggpageinfo = $this->ParseOggPageHeader();
     $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     switch ($info['audio']['dataformat']) {
         case 'vorbis':
             $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6);
             // hard-coded to 'vorbis'
             $this->ParseVorbisComments();
             break;
         case 'flac':
             $flac = new getid3_flac($this->getid3);
             if (!$flac->parseMETAdata()) {
                 $info['error'][] = 'Failed to parse FLAC headers';
                 return false;
             }
             unset($flac);
             break;
         case 'speex':
             $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
             $this->ParseVorbisComments();
             break;
     }
     // Last Page - Number of Samples
     if (!getid3_lib::intValueSupported($info['avdataend'])) {
         $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
     } else {
         $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
         $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
         if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
             $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
             $info['avdataend'] = $this->ftell();
             $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
             $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
             if ($info['ogg']['samples'] == 0) {
                 $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
                 return false;
             }
             if (!empty($info['audio']['sample_rate'])) {
                 $info['ogg']['bitrate_average'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
             }
         }
     }
     if (!empty($info['ogg']['bitrate_average'])) {
         $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
     } elseif (!empty($info['ogg']['bitrate_nominal'])) {
         $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
     } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
         $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
     }
     if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
         if ($info['audio']['bitrate'] == 0) {
             $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
             return false;
         }
         $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']);
     }
     if (isset($info['ogg']['vendor'])) {
         $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
         // Vorbis only
         if ($info['audio']['dataformat'] == 'vorbis') {
             // Vorbis 1.0 starts with Xiph.Org
             if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
                 if ($info['audio']['bitrate_mode'] == 'abr') {
                     // Set -b 128 on abr files
                     $info['audio']['encoder_options'] = '-b ' . round($info['ogg']['bitrate_nominal'] / 1000);
                 } elseif ($info['audio']['bitrate_mode'] == 'vbr' && $info['audio']['channels'] == 2 && $info['audio']['sample_rate'] >= 44100 && $info['audio']['sample_rate'] <= 48000) {
                     // Set -q N on vbr files
                     $info['audio']['encoder_options'] = '-q ' . $this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
                 }
             }
             if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
                 $info['audio']['encoder_options'] = 'Nominal bitrate: ' . intval(round($info['ogg']['bitrate_nominal'] / 1000)) . 'kbps';
             }
         }
     }
     return true;
 }
 private function EnsureBufferHasEnoughData($min_data = 1024)
 {
     if ($this->current_offset - $this->EBMLbuffer_offset >= $this->EBMLbuffer_length - $min_data) {
         if (!getid3_lib::intValueSupported($this->current_offset + $this->getid3->fread_buffer_size())) {
             $this->getid3->info['error'][] = 'EBML parser: cannot read past ' . $this->current_offset;
             return false;
         }
         fseek($this->getid3->fp, $this->current_offset, SEEK_SET);
         $this->EBMLbuffer_offset = $this->current_offset;
         $this->EBMLbuffer = fread($this->getid3->fp, max($min_data, $this->getid3->fread_buffer_size()));
         $this->EBMLbuffer_length = strlen($this->EBMLbuffer);
         if ($this->EBMLbuffer_length == 0 && feof($this->getid3->fp)) {
             $this->getid3->info['error'][] = 'EBML parser: ran out of file at offset ' . $this->current_offset;
             return false;
         }
     }
     return true;
 }
 function readEBMLint(&$string, &$offset, $dataoffset = 0)
 {
     $actual_offset = $offset - $dataoffset;
     if (!getid3_lib::intValueSupported($offset + $this->read_buffer_size)) {
         $this->warnings[] = 'aborting readEBMLint() because $offset larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     } elseif ($actual_offset >= strlen($string)) {
         $this->warnings[] = '$actual_offset > $string in readEBMLint($string[' . strlen($string) . '], ' . $offset . ', ' . $dataoffset . ')';
         return false;
     } elseif ($actual_offset < 0) {
         $this->warnings[] = '$actual_offset < 0 in readEBMLint($string[' . strlen($string) . '], ' . $offset . ', ' . $dataoffset . ')';
         return false;
     }
     $first_byte_int = ord($string[$actual_offset]);
     if (0x80 & $first_byte_int) {
         $length = 1;
     } elseif (0x40 & $first_byte_int) {
         $length = 2;
     } elseif (0x20 & $first_byte_int) {
         $length = 3;
     } elseif (0x10 & $first_byte_int) {
         $length = 4;
     } elseif (0x8 & $first_byte_int) {
         $length = 5;
     } elseif (0x4 & $first_byte_int) {
         $length = 6;
     } elseif (0x2 & $first_byte_int) {
         $length = 7;
     } elseif (0x1 & $first_byte_int) {
         $length = 8;
     } else {
         $this->warnings[] = 'invalid EBML integer (leading 0x00) at ' . $offset;
         $offset = PHP_INT_MAX + 1;
         // abort processing, skip to end of file
         return false;
     }
     $int_value = $this->EBML2Int(substr($string, $actual_offset, $length));
     $offset += $length;
     return $int_value;
 }
Пример #24
0
 function getid3_ogg(&$fd, &$ThisFileInfo)
 {
     $ThisFileInfo['fileformat'] = 'ogg';
     // Warn about illegal tags - only vorbiscomments are allowed
     if (isset($ThisFileInfo['id3v2'])) {
         $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
     }
     if (isset($ThisFileInfo['id3v1'])) {
         $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
     }
     if (isset($ThisFileInfo['ape'])) {
         $ThisFileInfo['warning'][] = 'Illegal APE tag present.';
     }
     // Page 1 - Stream Header
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
     $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
         $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first ' . GETID3_FREAD_BUFFER_SIZE . ' bytes (this might not be an Ogg-Vorbis file?)';
         unset($ThisFileInfo['fileformat']);
         unset($ThisFileInfo['ogg']);
         return false;
     }
     $filedata = fread($fd, $oggpageinfo['page_length']);
     $filedataoffset = 0;
     if (substr($filedata, 0, 4) == 'fLaC') {
         $ThisFileInfo['audio']['dataformat'] = 'flac';
         $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
         $ThisFileInfo['audio']['lossless'] = true;
     } elseif (substr($filedata, 1, 6) == 'vorbis') {
         $this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
     } elseif (substr($filedata, 0, 8) == 'Speex   ') {
         // http://www.speex.org/manual/node10.html
         $ThisFileInfo['audio']['dataformat'] = 'speex';
         $ThisFileInfo['mime_type'] = 'audio/speex';
         $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
         $ThisFileInfo['audio']['lossless'] = false;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8);
         // hard-coded to 'Speex   '
         $filedataoffset += 8;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
         $filedataoffset += 20;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
         $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
         $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
         $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
         $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
         $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate'];
         $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels'];
         if ($ThisFileInfo['speex']['vbr']) {
             $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
         }
     } else {
         $ThisFileInfo['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found neither';
         unset($ThisFileInfo['ogg']);
         unset($ThisFileInfo['mime_type']);
         return false;
     }
     // Page 2 - Comment Header
     $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
     $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     switch ($ThisFileInfo['audio']['dataformat']) {
         case 'vorbis':
             $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
             $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
             $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6);
             // hard-coded to 'vorbis'
             getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
             break;
         case 'flac':
             if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
                 $ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
                 return false;
             }
             break;
         case 'speex':
             fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
             getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
             break;
     }
     // Last Page - Number of Samples
     if (!getid3_lib::intValueSupported($ThisFileInfo['avdataend'])) {
         $ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
     } else {
         fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
         $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
         if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
             fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
             $ThisFileInfo['avdataend'] = ftell($fd);
             $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
             $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
             if ($ThisFileInfo['ogg']['samples'] == 0) {
                 $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
                 return false;
             }
             $ThisFileInfo['ogg']['bitrate_average'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
         }
     }
     if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
     } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
     } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
         $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
     }
     if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
         if ($ThisFileInfo['audio']['bitrate'] == 0) {
             $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
             return false;
         }
         $ThisFileInfo['playtime_seconds'] = (double) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']);
     }
     if (isset($ThisFileInfo['ogg']['vendor'])) {
         $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
         // Vorbis only
         if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
             // Vorbis 1.0 starts with Xiph.Org
             if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
                 if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
                     // Set -b 128 on abr files
                     $ThisFileInfo['audio']['encoder_options'] = '-b ' . round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
                 } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr' && $ThisFileInfo['audio']['channels'] == 2 && $ThisFileInfo['audio']['sample_rate'] >= 44100 && $ThisFileInfo['audio']['sample_rate'] <= 48000) {
                     // Set -q N on vbr files
                     $ThisFileInfo['audio']['encoder_options'] = '-q ' . $this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
                 }
             }
             if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
                 $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: ' . intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)) . 'kbps';
             }
         }
     }
     return true;
 }
 public function getLyrics3Data($endoffset, $version, $length)
 {
     // http://www.volweb.cz/str/tags.htm
     $info =& $this->getid3->info;
     if (!getid3_lib::intValueSupported($endoffset)) {
         $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $this->fseek($endoffset);
     if ($length <= 0) {
         return false;
     }
     $rawdata = $this->fread($length);
     $ParsedLyrics3['raw']['lyrics3version'] = $version;
     $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
     $ParsedLyrics3['tag_offset_start'] = $endoffset;
     $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
     if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
         if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
             $info['warning'][] = '"LYRICSBEGIN" expected at ' . $endoffset . ' but actually found at ' . ($endoffset + strpos($rawdata, 'LYRICSBEGIN')) . ' - this is invalid for Lyrics3 v' . $version;
             $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
             $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
             $length = strlen($rawdata);
             $ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
             $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
         } else {
             $info['error'][] = '"LYRICSBEGIN" expected at ' . $endoffset . ' but found "' . substr($rawdata, 0, 11) . '" instead';
             return false;
         }
     }
     switch ($version) {
         case 1:
             if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
                 $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
                 $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
             } else {
                 $info['error'][] = '"LYRICSEND" expected at ' . ($this->ftell() - 11 + $length - 9) . ' but found "' . substr($rawdata, strlen($rawdata) - 9, 9) . '" instead';
                 return false;
             }
             break;
         case 2:
             if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
                 $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6);
                 // LYRICSBEGIN + LYRICS200 + LSZ
                 $rawdata = $ParsedLyrics3['raw']['unparsed'];
                 while (strlen($rawdata) > 0) {
                     $fieldname = substr($rawdata, 0, 3);
                     $fieldsize = (int) substr($rawdata, 3, 5);
                     $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
                     $rawdata = substr($rawdata, 3 + 5 + $fieldsize);
                 }
                 if (isset($ParsedLyrics3['raw']['IND'])) {
                     $i = 0;
                     $flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
                     foreach ($flagnames as $flagname) {
                         if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
                             $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
                         }
                     }
                 }
                 $fieldnametranslation = array('ETT' => 'title', 'EAR' => 'artist', 'EAL' => 'album', 'INF' => 'comment', 'AUT' => 'author');
                 foreach ($fieldnametranslation as $key => $value) {
                     if (isset($ParsedLyrics3['raw'][$key])) {
                         $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
                     }
                 }
                 if (isset($ParsedLyrics3['raw']['IMG'])) {
                     $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
                     foreach ($imagestrings as $key => $imagestring) {
                         if (strpos($imagestring, '||') !== false) {
                             $imagearray = explode('||', $imagestring);
                             $ParsedLyrics3['images'][$key]['filename'] = isset($imagearray[0]) ? $imagearray[0] : '';
                             $ParsedLyrics3['images'][$key]['description'] = isset($imagearray[1]) ? $imagearray[1] : '';
                             $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
                         }
                     }
                 }
                 if (isset($ParsedLyrics3['raw']['LYR'])) {
                     $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
                 }
             } else {
                 $info['error'][] = '"LYRICS200" expected at ' . ($this->ftell() - 11 + $length - 9) . ' but found "' . substr($rawdata, strlen($rawdata) - 9, 9) . '" instead';
                 return false;
             }
             break;
         default:
             $info['error'][] = 'Cannot process Lyrics3 version ' . $version . ' (only v1 and v2)';
             return false;
             break;
     }
     if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end']) {
         $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
         unset($info['id3v1']);
         foreach ($info['warning'] as $key => $value) {
             if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
                 unset($info['warning'][$key]);
                 sort($info['warning']);
                 break;
             }
         }
     }
     $info['lyrics3'] = $ParsedLyrics3;
     return true;
 }
Пример #26
0
 function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // layer                                           2    always: '00'
     // protection_absent                               1
     // profile                                         2
     // sampling_frequency_index                        4
     // private_bit                                     1
     // channel_configuration                           3
     // original/copy                                   1
     // home                                            1
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = 0;
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; $i++) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!getid3_lib::intValueSupported($byteoffset)) {
             $ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond ' . ftell($fd) . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         fseek($fd, $byteoffset, SEEK_SET);
         // First get substring
         $substring = fread($fd, 10);
         $substringlength = strlen($substring);
         if ($substringlength != 10) {
             $ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset ' . (ftell($fd) - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // Initialise $AACheaderBitstream
         $AACheaderBitstream = '';
         // Loop thru substring chars
         for ($i = 0; $i < 10; $i++) {
             $AACheaderBitstream .= $decbin[$substring[$i]];
         }
         $bitoffset = 0;
         $synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12));
         $bitoffset += 12;
         if ($synctest != 0xfff) {
             $ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . (ftell($fd) - 10) . ' (found 0x0' . strtoupper(dechex($synctest)) . ' instead)';
             if ($ThisFileInfo['fileformat'] == 'aac') {
                 return true;
             }
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber > 0) {
             if (!$AACheaderBitstream[$bitoffset]) {
                 // MPEG-4
                 $bitoffset += 20;
             } else {
                 // MPEG-2
                 $bitoffset += 18;
             }
         } else {
             $ThisFileInfo['aac']['header_type'] = 'ADTS';
             $ThisFileInfo['aac']['header']['synch'] = $synctest;
             $ThisFileInfo['fileformat'] = 'aac';
             $ThisFileInfo['audio']['dataformat'] = 'aac';
             $ThisFileInfo['aac']['header']['mpeg_version'] = substr($AACheaderBitstream, $bitoffset, 1) == '0' ? 4 : 2;
             $bitoffset += 1;
             $ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
             $bitoffset += 2;
             if ($ThisFileInfo['aac']['header']['layer'] != 0) {
                 $ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x' . dechex($ThisFileInfo['aac']['header']['layer']) . ' instead';
                 return false;
             }
             $ThisFileInfo['aac']['header']['crc_present'] = substr($AACheaderBitstream, $bitoffset, 1) == '0' ? true : false;
             $bitoffset += 1;
             $ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
             $bitoffset += 2;
             $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']);
             $ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']);
             if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) {
                 $ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency'];
             $ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             $ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
             $bitoffset += 3;
             $ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration'];
             $ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             $ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) {
                 $ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
                 $bitoffset += 2;
             }
             if ($ReturnExtendedInfo) {
                 $ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
                 $ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
             } else {
                 $bitoffset += 2;
             }
         }
         $FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13));
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $ThisFileInfo['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         getid3_lib::safe_inc($ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $bitoffset += 13;
         $ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11));
         $bitoffset += 11;
         if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
         } else {
             $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
         }
         $ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2));
         $bitoffset += 2;
         if ($ThisFileInfo['aac']['header']['crc_present']) {
             //$ThisFileInfo['aac'][$framenumber]['crc']              = bindec(substr($AACheaderBitstream, $bitoffset, 16));
             $bitoffset += 16;
         }
         if (!$ReturnExtendedInfo) {
             unset($ThisFileInfo['aac'][$framenumber]);
         }
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $ThisFileInfo['avdataend']) {
             // keep scanning
         } else {
             $ThisFileInfo['aac']['frames'] = $framenumber;
             $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['avdataend'] / $byteoffset * ($framenumber * 1024 / $ThisFileInfo['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($ThisFileInfo['playtime_seconds'] == 0) {
                 $ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
             ksort($ThisFileInfo['aac']['bitrate_distribution']);
             $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'] . ' ' . $ThisFileInfo['aac']['header']['profile_text'];
             return true;
         }
     }
     // should never get here.
 }